JavaScriptでオブジェクト配列のグルーピング
 Author: 水卜

サンプルコード

オブジェクト配列をあるプロパティでグルーピングしたい時のサンプル。

const groupBy = (array, property) => {
  return array.reduce(
    (group, item) => Object.assign(group, {
      [item[property]]: (group[item[property]] || []).concat(item)
    }),
    {}
  )
}

解説

reduceの仕様

Array.prototype.reduceは、配列の各値にアクセスし、最終的に1つの値を返す。

// 第一引数にコールバック関数、第二引数に初期値
arr.reduce(callback[, initialValue]);
// コールバック関数をそのまま書くと以下。
arr.reduce(
  (accumulator, currentValue, currentIndex, array) => {
  },
  {}
);

callbackはaccumulator, currentValue, currentIndex, arrayの4つ引数をとる。
arr.reduceが呼ばれた時、コールバックのcurrentValueには配列の最初の要素が入る。

accumulator

accumulatorは初期値として空のオブジェクトが設定されている。

currentValue

currentValueは順番にアクセスしている配列の値が入る。
以下のforof文で言うとitemがここに入っている

for(const item of items) {};

currentIndex

currentIndexは前述のcurrentValueのインデックスである。
コールバック関数の中でarr[currentIndex - 1]のように記述すれば配列の前の値にアクセスすることもできる。

array

reduceをかけた配列がそのまま入っている。

accumulatorは「蓄積するもの」といった意味を持つ。
その名の通り、reduce関数は配列の各値にアクセスしながらaccumulatorに値を蓄積し、最後に蓄積しきったaccumulatorを得るといった使い方をする。

サンプルコードのコールバック

以上を踏まえてサンプルコードのコールバックを見てみよう。
accumulatorとcurrentValueしか使っていない。今回はオブジェクト配列のグルーピングがお題なので、currentValueにはオブジェクトが入っている。

(group, item) => Object.assign(group, {
    [item[property]]: (group[item[property]] || []).concat(item)
})

Object.assign(A, B)はAオブジェクトにBオブジェクトをコピー(同一のキーは上書き)し、Aオブジェクトを返すもの。
propertyをキーとしたオブジェクトを作成し、groupのpropertyにもしオブジェクトがあればitemをそこに追加し、なければ空の配列にitemを突っ込んだものを使っている。
これを配列の全要素で繰り返せば、propertyでグルーピングされたオブジェクトが手に入る。

lodashを使おう

なお、今までの話はlodashを使えば1秒で解決する。

npm install lodash
const _ = require('lodash');
_.groupBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }
 
// The `_.property` iteratee shorthand.
_.groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }