配列の重複するオブジェクトを取り除きつつ数える unique関数 group 関数 の実装

この記事は、下記記事のメント欄に書こうと思ったのですが、長すぎるコードになったので単独記事にしました。

配列の重複するオブジェクトを取り除きつつ数えたい - Qiita
https://qiita.com/torajiro1220/items/caac851ae3a3c4eb7fad

上記記事のデータ加工の1つめのものだけ対象です。

```js
const data = [
  {name: 'aaa', age: 18,},
  {name: 'bbb', age: 20,},
  {name: 'bbb', age: 21,},
  {name: 'ccc', age: 21,},
  {name: 'bbb', age: 20,},
];
```

上記のものを、下記のように加工したいということになります。

```js
[
  { name: 'aaa', age: 18, count: 1 },
  { name: 'bbb', age: 20, count: 2 },
  { name: 'bbb', age: 21, count: 1 },
  { name: 'ccc', age: 21, count: 1 },
],
```

元記事では reduce の例が載っています。

私は、reduceはあまり可読性がよくないと思っているので使わないようにしています。紹介するユニーク化やグループ化できる関数を作っておき再利用したりすると便利かと思います。

# unique関数

こんな感じの unique 関数作っています。外部関数で動作制御でき、detailフラグで結果だけを得るか詳細情報を得るかを分岐させています。

```js
const unique = (
  array, func = v => v, detail = false,
) => {
  const index = [];
  const result = [];
  const count = [];
  array.forEach(v => {
    const funcResult = func(v);
    const indexResult = index.indexOf(funcResult);
    if (indexResult === -1) {
      index.push(funcResult);
      result.push(v);
      count.push(1);
    } else {
      count[indexResult] += 1;
    }
  });
  if (detail) {
    return { index, result, count };
  }
  return result;
};

const data = [
  {name: 'aaa', age: 18,},
  {name: 'bbb', age: 20,},
  {name: 'bbb', age: 21,},
  {name: 'ccc', age: 21,},
  {name: 'bbb', age: 20,},
];

console.log(
  unique(data, d => d.name, {detail: true}),
);
// {
//   index: ['aaa', 'bbb', 'ccc'],
//   result: [
//     { name: 'aaa', age: 18 },
//     { name: 'bbb', age: 20 },
//     { name: 'ccc', age: 21 }
//   ],
//   count: [1, 3, 1]
// }

var result = unique(data, d => d.name, {detail: true});
console.log(
  result.result.map((e, i) => ({ name: e.name, count: result.count[i]})),
);
// [
//   { name: 'aaa', count: 1 },
//   { name: 'bbb', count: 3 },
//   { name: 'ccc', count: 1 },
// ],

console.log(
  unique(data, d => d.name + d.age.toString(), {detail: true}),
);
// {
//   index: ['aaa18', 'bbb20', 'bbb21', 'ccc21'],
//   result: [
//     { name: 'aaa', age: 18 },
//     { name: 'bbb', age: 20 },
//     { name: 'bbb', age: 21 },
//     { name: 'ccc', age: 21 }
//   ],
//   count: [1, 2, 1, 1]
// }

var result = unique(
  data, d => d.name + d.age.toString(), {detail: true}
);
console.log(
  result.result.map((e, i) => ({ name: e.name, age: e.age, count: result.count[i] })),
);
// [
//   { name: 'aaa', age: 18, count: 1 },
//   { name: 'bbb', age: 20, count: 2 },
//   { name: 'bbb', age: 21, count: 1 },
//   { name: 'ccc', age: 21, count: 1 },
// ],
```

こちらで動作確認可能です。
https://jsbin.com/vazijipiqe/edit?html,js,console


# group関数

また、このようなgroup関数を使っても同じデータ加工ができます。


```js
const group = (
  array, func = v => v, detail = false
) => {
  const index = [];
  const result = [];
  array.forEach(v => {
    const funcResult = func(v);
    const i = index.indexOf(funcResult);
    if (i === -1) {
      index.push(funcResult);
      result.push([v]);
    } else {
      result[i].push(v);
    }
  });

  if (detail) {
    return { index, result };
  }
  return result;
};

const data = [
  {name: 'aaa', age: 18,},
  {name: 'bbb', age: 20,},
  {name: 'bbb', age: 21,},
  {name: 'ccc', age: 21,},
  {name: 'bbb', age: 20,},
];

console.log(
  group(data, d => d.name, {detail: true}),
);
// {
//   index: ['aaa', 'bbb', 'ccc'],
//   result: [
//     [{ name: 'aaa', age: 18 }],
//     [
//       { name: 'bbb', age: 20 },
//       { name: 'bbb', age: 21 },
//       { name: 'bbb', age: 20 },
//     ],
//     [{ name: 'ccc', age: 21 }]
//   ]
// }

console.log(
  group(data, d => d.name, {detail: true})
    .result.map(e => ({ name: e[0].name, count: e.length })),
);
// [
//   { name: 'aaa', count: 1 },
//   { name: 'bbb', count: 3 },
//   { name: 'ccc', count: 1 },
// ]

console.log(
  group(data, d => d.name + d.age.toString(), {detail: true}),
);
// {
//   index: ['aaa18', 'bbb20', 'bbb21', 'ccc21'],
//   result: [
//     [{ name: 'aaa', age: 18 }],
//     [
//       { name: 'bbb', age: 20 },
//       { name: 'bbb', age: 20 },
//     ],
//     [
//       { name: 'bbb', age: 21 },
//     ],
//     [{ name: 'ccc', age: 21 }],
//   ],
// }

console.log(
  group(data, d => d.name + d.age.toString(), {detail: true})
    .result.map(e => ({ name: e[0].name, age: e[0].age, count: e.length })),
);
// [
//   { name: 'aaa', age: 18, count: 1 },
//   { name: 'bbb', age: 20, count: 2 },
//   { name: 'bbb', age: 21, count: 1 },
//   { name: 'ccc', age: 21, count: 1 },
// ],

```

こちらで動作確認可能です。
https://jsbin.com/zikeniteva/edit?html,js,console

## まとめ
unique と group は、用途が違うものですが、汎用的に使えるのでこのようなデータ加工を簡単に行うことができました。

unique も group も、自作ライブラリの Parts.js に搭載しています。プロジェクトの開発を楽にしたい方向けの便利関数を多く用意しているので、よかったら使ってみてください。マニュアル作れて無いのでテストコードみないと動きがわからないので、結構上級者用かもですが、直感的に理解できる感じの動作をする関数群を用意しています。

関数実装のサンプルなどや、WebPackのビルド設定参考などにも、どうぞです。

standard-software/partsjs
https://github.com/standard-software/partsjs