JavaScriptでPython風のzip_longest関数を実装する

こちらの記事でPython風のzip関数を実装しました。

yucatio.hatenablog.com

今回はzip_longest関数を作成します。以下のように、各配列の同じインデックスの要素をまとめます。

const a1 = [1, 2, 3]
const a2 = ["Jan", "Feb", "Mar"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"]]

各配列の長さが異なる場合には、一番長い配列の長さになります。未定義値はundefinedになります。

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"], [4, "Apr", undefined], [undefined, "May", undefined]]

zip_longest関数本体

zip_longest関数の実装です。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
}

コードの解説

ほとんどzip関数の解説と同じです。

まず、関数の定義の部分を解説します。レスト構文を使用して、引数全てをarraysに格納します。

const zip_longest = (...arrays) => {

}

例えば、

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)

と呼び出したとき、arrays

[
  [1, 2, 3, 4],
  ["Jan", "Feb", "Mar", "Apr", "May"],
  ["Garnet", "Amethyst", "Aquamarine"]
]

です。以下、引数にこの配列を渡したときの動作を説明します。

zip_longest関数では、一番長い配列の長さに合わせるので、まず一番長い配列の長さを求めます。

まず、各配列の長さを、求めます。

const zip_longest = (...arrays) => {
  arrays.map(arr => arr.length)
  #=> [4, 5, 3]
}

この中の最大値は、Math.maxとスプレッド演算子を使用して、以下のように書けます。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  #=> const length = Math.max(...[4, 5, 3])
  #=> const length = Math.max(4, 5, 3)
  #=> const length = 5
}

5回繰り返すので、new Array(length).fill().map((_, i) => i)の構文を使用します。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  new Array(length).fill().map((_, i) => i))
  #=> [0, 1, 2, 3, 4]
}

fillをはさむ理由については、こちらの記事をご覧ください。

yucatio.hatenablog.com

各配列のi番目の要素は、

arrays.map(arr => arr[i])

で取得することができます。よくわからない場合は、iではなく、012など具体的な数字で考えるとよいです。例えば、各配列の0番目の要素は、

arrays.map(arr => arr[0])
#=> [1, "Jan""Garnet"]

です。

これをmapに渡す関数の戻り値にします。

JavaScriptの場合、配列の長さより大きいインデックスを指定した場合はundefinedになります。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
  #=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"], [4, "Apr", undefined], [undefined, "May", undefined]]
}

以上でzip_longest関数の完成です。

補足的な話題はzip関数の方の記事に記載していますので、あわせてご覧ください。

yucatio.hatenablog.com