コーディングサイト(AtCoderなど)で勉強になったと思う書き方(主にPython, JavaScript, TypeScript)
PythonとJavaScriptでsortする際に複数の項目で並び替える時の違い
例えば
['Hiroshi','Kenji','Masahiro','Yutaka','Shigeru','Kazuo','Noboru','Satoshi','Yoshio']
のような配列があった場合に、文字の長さで並び替えることを最優先し、その後アルファベットの並び替えは降順で文字を並び替えたい場合に、Python
と JavaScript
の記述の違いを記述する。
Python
の場合、
lst = ['Hiroshi','Kenji','Masahiro','Yutaka','Shigeru','Kazuo','Noboru','Satoshi','Yoshio']
print(sorted(lst,key=lambda s:(len(s),s),reverse=True))
上記のように記述すると、lambda
式の ()
の中で先に記述したものが優先的にソートされる。
JavaScript
の場合
const lst = ['Hiroshi','Kenji','Masahiro','Yutaka','Shigeru','Kazuo','Noboru','Satoshi','Yoshio']
console.log([...lst].sort((a,b) => b.length - a.length || b.localeCompare(a)))
と記述できる。Python の方は頻繁に使う書き方ではあるが、JavaScriptで ||
を使うことで先に記述した b.length - a.length
の方を優先的に記述できるのは便利な記述だと思った。
b.localeCompare(a)
は b > a
でも同じ結果が得られるようになるが、localeCompareを使った方がコードの可読性が増すのでこちらで記述するのがbetter。
PythonとJavaScriptで文字列を int型に変換するとき、エラーになる場合とその対処法
Python
であれば 空の文字列を int
を用いて int型
に変換しようとするときエラーになり、JavaScript
であれば parseInt
を使えば空の文字列は NaN
と変換される。
そのときはそれぞれ or
と ||
を用いればエラーを回避できる
Python
int("" or 0)
# orの後に出力したい数字を指定する
JavaScript
parseInt("" || 0)
// ||のあとに出力したい数字を指定する
JavaScriptとPythonを用いて文字列を置換処理をさせるときの第2引数について
今まで両言語ともに文字列を置換させるときには第二引数に置換後の文字列を記述するのがほとんどで、
Pythonで re.sub
を用いて文字を置換処理させるときには、re.subの第二引数の値は置換後の文字列だと思っていたが、それは間違いで 公式(https://docs.python.org/ja/3/library/re.html) を読んでみると
もし repl が関数であれば、重複しない pattern が発生するたびにその関数が呼ばれます。
この関数は一つの Match 引数を取り、置換文字列を返します
という記述があり、第二引数には関数を渡すことができる。
例えばある文字列のなかで、文字の長さが3以上のときには文字列の先頭を大文字にする(capitalize)と考えた時、
例
"apple of banana" => "Apple of Banana"
以下のように記述できる。
#sが対象となる文字列
re.sub(r'\S{3,}', lambda m: m.group(0).title(), s)
そしてJavaScriptと関しても同様で第二引数に関数を渡すことができ、こちらは replace
関数を使えるので便利である。
s.replace(/\S{3,}/g, w => w.slice(0, 1).toUpperCase() + w.slice(1).toLowerCase());
両言語ともに第二引数の関数の引数として適時マッチした文字列が渡されて、それに対して処理を行なっていく。
今まで内包表記などで ifで判定していたことがほとんどだったのでこのように一行で書けるととても綺麗なコードになる。
Pythonで zip_longest の使い方
Pythonで異なる長さの配列で長い配列の方に合わせてloop
を作成するときには zip
ではなく zip_longest
を使う。ただ、長さが違う時、短い配列がindexの最後まで到達してしまうとそのあとはNone
が出力される。
['a', 'cat']
のように長さが異なる配列からそれぞれ文字を取り出し、index毎に文字を連結し、indexが末尾に到達した配列については空白文字を付与するような以下のタスクがある場合を考える。
"a c\n a\n t"
その時 zip_longest
の引数として fillvalue
が使える
itertools.zip_longest(*iterables, fillvalue=None)
今回の場合空白文字なので、値としては空白文字を与えると以下のような実装になる。
"\n".join(f"{a} {b}" for a, b in zip_longest(*arr, fillvalue=" "))
JavaScriptで、pythonのようにnumpyのmean関数を用いて列方向の平均値を求める方法
Pythonでは
[ [1, 2, 3, 4], [5, 6, 7, 8] ]
のような配列があり、それらの列方向の合計を求める際にはnumpy配列に変換した上で
配列.mean(axis=0)
を用いれば求められる
JavaScriptで特にライブラリなどを用いない場合、reduceを用いて実装することができる
const arr = [ [1, 2, 3, 4], [5, 6, 7, 8] ]
const meanArray = arr[0].map((_, colIndex) =>
arr.reduce((sum, row) => sum + row[colIndex], 0) / arr.length
);
return meanArray;
まず、arr[0]
で列方向に繰り返すloopを作成する。その後、reduce
にて 行ごとの値を取り出す。
row
には arrの要素ごとに値を取り出すため、[1, 2, 3, 4]
、[5, 6, 7, 8]
のように一次元配列が格納された形になるが、row[colIndex]
と記載することで列ごとに値を取り出す形となり、それらを足し合わせて、行の長さで割ることで各列ごとの平均を算出することが可能となる。
JavaScriptにおいてprototypeを定義するときはアロー関数を使えない
当たり前のようにアロー関数を使っていると、prototype時にアロー関数を定義してしまうことがあるが、エラーになるので原因を調べてみました。
アロー関数が使えない理由は、アロー関数がthisのスコープを持たないから。
普通の関数ではthisが呼び出し元のコンテキストを指しますが、アロー関数では定義時のスコープをそのまま保持し、動的に変わらない。
なので、String.prototype.myNewMethod = () => { ... }
とすると、this
が undefined
またはグローバルオブジェクトを指してしまい、意図したオブジェクトを指しません。
例えば:
String.prototype.myNewMethod = () => {
console.log(this); // undefined or global object
return this.toUpperCase(); // Error: this.toUpperCase is not a function
}
対して、通常の関数を使うと正しく this
が現在のインスタンスを指します:
String.prototype.myNewMethod = function() {
console.log(this); // the string instance
return this.toUpperCase();
}
TypeScriptにおけるfind関数の考え方
例えば
[ 1, 1, 1, 2, 1, 1 ]
のような配列から値が唯一異なる数字(今回であれば2)を取り出したい時
arr.find((value,index) => arr.indexOf(value) === arr.lastIndexOf(value))
のように find
関数を使うことが便利なように思えるが、TypeScript
のように型を定義して値を返却したい場合、記載 のように、値が見つからない場合は undefined
を返すため undefined
を型定義に含めないといけない。
なので、TypeScript
においては filter
関数を定義するほうがよい。
arr.filter((el) => arr.indexOf(el) == arr.lastIndexOf(el))[0];