JavaScriptでRubyのeach_sliceと同じことをしたい

こうしたい

eachSlice([1, 2, 3, 4, 5, 6], 2) => [[1, 2], [3, 4], [5, 6]]

残念ながらArray.prototypeにはeach_sliceと同じ動作をするメソッドが定義されてないので自分で実装する。

再帰(破壊的)

const eachSlice = (arr, n = 2, result = []) => {
  if (arr.length === 0) {
    return result
  }

  return eachSlice(arr, n, [...result, arr.splice(0, n)])
}

// [...result, arr.splice(0, n)]が分かりにくいなら
const eachSlice = (arr, n = 2, result = []) => {
  if (arr.length === 0) {
    return result;
  }

  result.push(arr.splice(0, n))
  return eachSlice(arr, n, result)
}

const arr = [1, 2, 3, 4, 5, 6]

eachSlice([...arr], 2) // => [[1, 2], [3, 4], [5, 6]]
eachSlice([...arr], 3) // => [[1, 2, 3], [4, 5, 6]]
eachSlice([...arr], 5) // => [[1, 2, 3, 4, 5], [6]]

Array<number>の要素数が16000くらいでスタックオーバーフローが発生するので注意。
普段フロントエンドでそこまで大きい配列を扱う事がないので考慮してない。

非破壊的にする

const eachSliceExecute = (arr, n = 2, result = []) => {
  if (arr.length === 0) {
    return result
  }

  return eachSliceExecute(arr, n, [...result, arr.splice(0, n)])
}

const eachSlice = (arr, n) => eachSliceExecute([...arr], n)

While

const eachSlice = (arr, n = 2) => {
  let dup = [...arr]
  let result = [];
  let length = dup.length;

  while (0 < length) {
    result.push(dup.splice(0, n));
    length = dup.length
  }

  return result;
};

スタックオーバーフローを回避したいならwhileを使う。

おまけ

カリー化

const eachSlice = n => {
  return function(arr) {
    return eachSliceExecute([...arr], n)
  }
}

const arr = [1, 2, 3, 4, 5, 6]

const eachSlice2 = eachSlice(2)
const eachSlice5 = eachSlice(5)

eachSlice2(arr) // => [[1, 2], [3, 4], [5, 6]]
eachSlice5(arr) // => [[1, 2, 3, 4, 5], [6]]

CodeSandbox

参考