📚

【JavaScript】配列の重複を取り除く

2023/10/18に公開

はじめに

JavaScriptにおいて、Setを用いてオブジェクト配列の重複を削除しようとするとうまくいかず、filter()関数を用いることで実現することができました。
せっかくなので、プリミティブな配列の場合、オブジェクト配列の場合において重複を削除する方法をまとめてみようと思い記事を書くことにしました。
また、コールバック関数を複数使う場合少しわかりづらいので、自分なりの解説を付け加えてみました。
特に初心者エンジニアのためになれば幸いです!

プリミティブな配列の場合

Setオブジェクトを使うだけで超簡単にできます!

const array = [1, 1, 2, 3, 4, 4, 5, 5, 5, 6];
const uniqueArray = Array.from(new Set(array));
console.log(uniqueArray);
// => [1, 2, 3, 4, 5, 6];

解説

SetはES2015から追加されたコンストラクタで、一意の値を格納するオブジェクトを作成します。
配列から作成することもでき、その際には重複が削除されたSetオブジェクトが作成されます。
上記の場合、
new Set(array){1, 2, 3, 4, 5, 6}を表します。
あとはArray.from()関数で配列に再変換するだけです。

オブジェクト配列の場合

配列の中身がオブジェクトの場合、上記のSetを使ってもうまくいきません。
この場合、filter()関数およびfindIndex()関数を用いればうまくいきます!

const array = [
    { name: "Alice", age: 30 },
    { name: "Bob", age: 25 },
    { name: "Charlie", age: 35 },
    { name: "Alice", age: 30 },
    { name: "Bob", age: 25 },
    { name: "David", age: 40 },
    { name: "David", age: 40 }
];
const uniqueArray = array.filter((element, index, self) => self.findIndex(e => e.name === element.name) === index);
console.log(uniqueArray);
// => [ {name: 'Alice', age: 30}, {name: 'Bob', age: 25}, {name: 'Charlie', age: 35}, {name: 'David', age: 40}]

上記の関数ではオブジェクトのnameが一致するオブジェクトを重複するオブジェクトとみなし、2回目以降に登場するものを削除しています。

解説

  • filter()関数
    filter()関数はmap()などと同様で、配列から配列を生成する関数です。
    配列に対して使い、引数にコールバック関数を取ります。元の配列に対して、中の関数がtrueの場合のみをフィルタリングして新たな配列を作ります!
    中の関数は第一引数が配列の要素、第二引数がindex、第三引数が配列そのものを取ります。今回は全部使っています。

  • findIndex()関数
    findIndex()関数も配列に対して使うメソッドで、引数にコールバック関数を取ります。
    中の関数がtrueとなる一番最初のindexを返す関数です。

したがって、上記のスクリプトではarrayの各要素elementに対して、filter()関数で(element, index, self) => self.findIndex(e => e.name === element.name) === index)がtrueであるもののみをフィルタリングして配列再生成をしています。
その中身とはというと、findIndex()でnameが一致する最初の要素のindexを取り出して、elementがarrayの中で一番最初に登場する要素であるかどうかを判断しています。
各要素elementにおいて、そのnameが2回目以降の登場だった場合はself.findIndex(e => e.name === element.name)indexの値が異なるため、filter()の結果がfalseになり、uniqueArrayには追加されません。

もちろん、findIndex()内のコールバック関数を書き換えることで、オブジェクト重複の定義を自由に設定できます。
より汎用的な形で書くとしたらこのような形でしょうか。

function areObjectsEqual(obj1, obj2) {
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false;
  }
  for (const key in obj1) {
    if (obj1[key] !== obj2[key]) {
      return false;
    }
  }
  return true;
}

const array = [
    { name: "Alice", age: 30 },
    { name: "Bob", age: 25 },
    { name: "Charlie", age: 35 },
    { name: "Alice", age: 30 },
    { name: "Bob", age: 25 },
    { name: "David", age: 45 },
    { name: "David", age: 40 }
];

const uniqueArray = array.filter((element, index, self) => self.findIndex(e => areObjectsEqual(e, element)) === index);
console.log(uniqueArray);
// => [ {name: 'Alice', age: 30}, {name: 'Bob', age: 25}, {name: 'Charlie', age: 35}, {name: 'David', age: 45}, {name: 'David', age: 40}]

nameだけでなくageも重複したもののみを取り出すことができました!

まとめ

プリミティブな配列の重複を取り除くにはSetを使えば簡単にできます!
オブジェクト配列については、filter()メソッドを使いましょう!

参考文献

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Set
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

Discussion