JavaScriptでn個ずつ配列を分割する

JavaScriptで配列を指定された個数ずつに分割します。

例えば、

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

という配列を3個ずつ分割するのであれば、

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

という配列になります。

実装方針

配列から一部を通り出すのには、 Array.prototype.slice() - JavaScript | MDN という関数が使えます。 この関数は、開始のインデックスと終わりのインデックスを渡すと、開始のインデックスから終わりのインデックスの1つ前までの部分の配列を返します。 pythonmylist[begin:end]rubyarray[begin...end]のような動作をします。

例えば、

const array=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

であれば、

array.slice(3, 6),

は、3、4、5のインデックスを含むので

[4, 5, 6]

という配列が返されます。

また、

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

という配列をこのように3個ずつ分割するのを、

[[1,2,3], [4,5,6], [7,8,9], [10]]

slice()を使用して書くと、

const result = [
  array.slice(0, 3),
  array.slice(3, 6),
  array.slice(6, 9),
  array.slice(9, 10)
]

となります。slice()の2つめの引数に配列の数より大きな値が指定された場合は、array.lengthが指定されたのと同じになりますので、下記のように書き換えても同様の結果になります。最後の要素を、array.slice(9, 10)からarray.slice(9, 12)に変更しました。これで各sliceの引数が全て3の倍数となりました。

const result = [
  array.slice(0, 3),
  array.slice(3, 6),
  array.slice(6, 9),
  array.slice(9, 12)
]

ところで、最終的な配列のサイズは、 分割する対象の配列を3で割って、端数が出たら切り上げた数です。今回分割する対象の配列の長さは10ですので、10/3=3.333..これを切り上げて4が最終的に作成される配列の長さです。 JavaScriptで切り上げを行うには、 Math.ceil() - JavaScript | MDN を使用します。

インデックスを3ずつずらしながら、array.slice()を、作成する配列の長さぶん繰り返せば、配列を分割することができそうです。

コード

コードをステップバイステップで作成します。

はじめに関数を定義します。引数に分割対象の配列と、いくつずつに分割するか指定します。

const sliceByNumber = (array, number) => {
  console.log('array', array)
  console.log('number', number)
  return array
}

console.log(sliceByNumber([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3))

実行結果です。疎通確認が完了しました。

array [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
number 3
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

作成する配列のサイズを計算します。

const sliceByNumber = (array, number) => {
  const length = Math.ceil(array.length / number)
  console.log('length', length)
  return array
}

console.log(sliceByNumber([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3))

実行結果です。作成する配列の長さ"4"が取得できました。

length 4
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

次に、長さ4の配列を作成し、配列の各要素に対してmapを呼び出します。この時、配列のインデックスの数にnumberを掛けたものを表示します。

const sliceByNumber = (array, number) => {
  const length = Math.ceil(array.length / number)
  return new Array(length).fill().map((_, i) => {
    console.log(`i=${i}, i*number=${i*number}, (i+1)*number=${(i+1)*number}`)
    return i
  })
}

console.log(sliceByNumber([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3))

Array(n)map()の間にfill()を挟む理由については、こちらの記事を参照してください。

yucatio.hatenablog.com

実行結果です。Array.sliceに渡す数が取得できました。

i=0, i*number=0, (i+1)*number=3
i=1, i*number=3, (i+1)*number=6
i=2, i*number=6, (i+1)*number=9
i=3, i*number=9, (i+1)*number=12
[0, 1, 2, 3]

最後に、i*number(i+1)*numberArray.sliceに渡せば、欲しい配列が出来上がります。

const sliceByNumber = (array, number) => {
  const length = Math.ceil(array.length / number)
  return new Array(length).fill().map((_, i) =>
    array.slice(i * number, (i + 1) * number)
  )
}

console.log(sliceByNumber([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3))

実行結果です。配列が3個ずつに分割されています。最後は要素数が1になっています。

[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
  [10]
]

array.lengthnumberで割り切れる例です。長さ10の配列を5個ずつに分割しました。

[
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
]

以上でn個ずつ配列を分割することができました。