LINDORのAdvent Calendar(本物)の18日目を開けたところ。
配列とかおれおれAdvent Calendar2018 – 18日目

ありそうでないメソッド、それが重複排除です。

Underscore.jsやLodashには uniq() てのがあって、こんな感じ↓で使えます。

const arr0 = [11, 22, 11, 22, 33];
const arr1 = _.uniq(arr0);

JavaScriptにはありません。

Set を使う

単純な値ならこれが一番簡単。

Set は重複しない値を格納できるオブジェクトです。 ... で展開して配列オブジェクトへ再変換します。

const arr0 = [11, 22, 11, 22, 33];
const arr1 = [...new Set(arr0)];
console.log(arr1); // => [ 11, 22, 33 ]

includes() で確認する

これが一番素直。

includes() は配列が指定の要素を含むかどうか調べるやつです。 重複してなかったら追加するだけ。

const arr0 = [11, 22, 11, 22, 33];

const arr1 = arr0.reduce((a, v) => {
  if (!a.includes(v)) {
    a.push(v);
  }
  return a;
}, []);

console.log(arr1); // => [ 11, 22, 33 ]

reduce() に自信がなければ forEach() でも。

const arr1 = [];
arr0.forEach((v) => {
  if (!arr1.includes(v)) {
    arr1.push(v);
  }
});

some() でやる

Set も includes() もオブジェクトの場合は完全に同じインスタンスでないと反応しないので、その場合は some() がよろしいかと。

もっと自由度が高い。

const arr0 = [
  { id: '11', num: 1 },
  { id: '22', num: 2 },
  { id: '11', num: 3 },
  { id: '22', num: 4 },
  { id: '33', num: 5 },
];

const arr1 = arr0.reduce((a, v) => {
  if (!a.some((e) => e.id === v.id)) {
    a.push(v);
  }
  return a;
}, []);

console.log(arr1);
// [ { id: '11', num: 1 },
//   { id: '22', num: 2 },
//   { id: '33', num: 5 } ]

重複分は先に出てくるやつが優先です。

Map を使う

これもオブジェクトに対応。初見で「ぎょえー」てなりそう。

const arr0 = [
  { id: '11', num: 1 },
  { id: '22', num: 2 },
  { id: '11', num: 3 },
  { id: '22', num: 4 },
  { id: '33', num: 5 },
];

const arr1 = [...new Map(arr0.map((v) => [v.id, v])).values()];

console.log(arr1);
// [ { id: '11', num: 3 },
//   { id: '22', num: 4 },
//   { id: '33', num: 5 } ]

仕組みは Set と some() の組み合わせです、だいたい。

  1. map() でIDとオブジェクト本体の組み合わせへ変換
  2. 組み合わせを元に Map オブジェクトを作成。ここでID( Map のキー)が重複するものは排除)
  3. values() でオブジェクト( Map の値)のみの反復子を得る
  4. スプレッド構文 ... を伴う配列初期化子 [] で配列に。完成

重複分は後ろにあるやつが優先(上書き)です。

おしまい

なんか良いのある?

参考