Open7

コーディングサイト(AtCoderなど)で勉強になったと思う書き方(主にPython, JavaScript, TypeScript)

tac-tac-go@rm -rf /tac-tac-go@rm -rf /

PythonとJavaScriptでsortする際に複数の項目で並び替える時の違い

例えば
['Hiroshi','Kenji','Masahiro','Yutaka','Shigeru','Kazuo','Noboru','Satoshi','Yoshio']
のような配列があった場合に、文字の長さで並び替えることを最優先し、その後アルファベットの並び替えは降順で文字を並び替えたい場合に、PythonJavaScript の記述の違いを記述する。

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。

tac-tac-go@rm -rf /tac-tac-go@rm -rf /

PythonとJavaScriptで文字列を int型に変換するとき、エラーになる場合とその対処法

Pythonであれば 空の文字列を int を用いて int型 に変換しようとするときエラーになり、JavaScript であれば parseInt を使えば空の文字列は NaN と変換される。
そのときはそれぞれ or|| を用いればエラーを回避できる

Python

int("" or 0)
# orの後に出力したい数字を指定する

JavaScript

parseInt("" || 0)
// ||のあとに出力したい数字を指定する
tac-tac-go@rm -rf /tac-tac-go@rm -rf /

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で判定していたことがほとんどだったのでこのように一行で書けるととても綺麗なコードになる。

tac-tac-go@rm -rf /tac-tac-go@rm -rf /

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=" "))
tac-tac-go@rm -rf /tac-tac-go@rm -rf /

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] と記載することで列ごとに値を取り出す形となり、それらを足し合わせて、行の長さで割ることで各列ごとの平均を算出することが可能となる。

tac-tac-go@rm -rf /tac-tac-go@rm -rf /

JavaScriptにおいてprototypeを定義するときはアロー関数を使えない

当たり前のようにアロー関数を使っていると、prototype時にアロー関数を定義してしまうことがあるが、エラーになるので原因を調べてみました。

アロー関数が使えない理由は、アロー関数がthisのスコープを持たないから。
普通の関数ではthisが呼び出し元のコンテキストを指しますが、アロー関数では定義時のスコープをそのまま保持し、動的に変わらない。

なので、String.prototype.myNewMethod = () => { ... } とすると、thisundefined またはグローバルオブジェクトを指してしまい、意図したオブジェクトを指しません。

例えば:

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();
}
tac-tac-go@rm -rf /tac-tac-go@rm -rf /

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];