Javascriptでオブジェクトの配列の重複を削除

概要

JavaScriptにおいて、通常の配列の場合はSetを用いると重複を削除できます。

const arraySrc = [1, 2, 3, 3, 1, 4, 4];
const arrayDist = Array.from(new Set(arraySrc));

// [1, 2, 3, 4]
console.log(arrayDist);

オブジェクトの配列の場合

配列の中身がオブジェクトの場合、先ほどのSetは使えません。
オブジェクトの配列の場合、map()またはfilter()及びindexOfを用います。

map()を用いる

const humans = [
  {"id": 1, "name": "taro"},
  {"id": 2, "name": "jiro"},
  {"id": 3, "name": "saburo"},
  {"id": 1, "name": "shiro"}
];

const uniqueHumans = Array.from(
  new Map(humans.map((human) => [human.id, human])).values()
);

// [{id: 1, name: "shiro"},{id: 2, name: "jiro"},{id: 3, name: "saburo"}
console.log(uniqueHumans);

データ量が多い場合は、こちらを使う方が良いらしいです。

filter()findIndexを用いる

const humans = [
  {"id": 1, "name": "taro"},
  {"id": 2, "name": "jiro"},
  {"id": 3, "name": "saburo"},
  {"id": 1, "name": "shiro"}
];

const uniqueHumans = humans.filter(
  (human, index, self) => self.findIndex((item) => item.id === human.id) === index
);

// [{id: 1, name: "taro"},{id: 2, name: "jiro"},{id: 3, name: "saburo"}]
console.log(uniqueHumans);

要素数が小さい配列を多数処理する際は、こちらを使う方が良いらしいです。

それぞれの処理で結果が異なる理由

この2つの方法は、どちらも配列内のオブジェクトの重複を削除することを目的としていますが、重複が発生した際の保持するオブジェクトの選定方法が異なるため、結果が異なります。

Mapを用いる場合

この方法は次のように動作します:

  1. humans配列をmap関数で、human.idをキー、humanオブジェクトを値とする配列に変換します。
  2. この配列をMapコンストラクタに渡します。Mapはキーの重複を許さないため、同じidのオブジェクトが現れると、そのオブジェクトは最新のもので上書きされます。
  3. Mapのvaluesメソッドで、ユニークな値のIteratorを取得し、それをArray.fromで配列に変換します。

この方法では、重複するidがある場合、最後に現れたオブジェクトが保持されます。

filterfindIndexを用いる場合

この方法は次のように動作します:

  1. humans配列をfilter関数でフィルタリングします。
  2. フィルタリング条件として、配列の各要素が初めて出現する位置(インデックス)が現在のインデックスと同じかどうかをチェックします。
  3. 同じidを持つオブジェクトがすでに存在する場合、条件が偽となり、そのオブジェクトはフィルタリングされます。

この方法では、重複するidがある場合、最初に現れたオブジェクトが保持されます。

どちらを使えば良いの?

どちらを使えば良いかは重複した場合にどのオブジェクトを保持したいかに依存します。

  • 最後に現れたオブジェクトを保持したい場合は、Mapを用いる方法が適しています。
  • 最初に現れたオブジェクトを保持したい場合は、filterを用いる方法が適しています。

要件に応じて適切な方法を選択して下さい。