はじめに
単純な配列は以下のようにSet
を使うと重複を削除できるというのがよく知られてます。
const numbers = new Set([1, 2, 1, 1])
const uniqueNumbers= Array.from(numbers)
//uniqueNumbers は [1, 2]
以下のようなオブジェクトの配列の重複を削除する方法はあまり紹介されていなかったのでメモとして書きます。
const users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}, {"id": 1, "name": "Alice"}, {"id": 1, "name": "Alice"}]
// [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}] にしたい
Mapを使って重複を削除する
データ量が多いときはArray.prototype.filter()ではなくMap
を使いましょう。(理由は後述)
Map
はArray.prototype.map()ではなくMapのことです。
結論から言うとコードは以下のようになります。
const users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}, {"id": 1, "name": "Alice"}, {"id": 1, "name": "Alice"}]
const uniqueUsers = Array.from(
new Map(users.map((user) => [user.id, user])).values()
);
// uniqueUsers は [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
Map
について詳しくは他の記事で紹介されているので、ここでは簡単に説明します。
Map
はkey-value
型のオブジェクトで、任意の値をkeyとして使用することができます。
先程のコードだと、user
のid
がkeyでuser
がvalueとなるようにMap
を作成します。
Map
では同じkeyに違う値を設定できないので、Map
を作成する際に重複が削除されます。
const userMap = new Map(users.map((user) => [user.id, user]))
// 以下のようなイメージになります(厳密にはこのようなオブジェクトではないです。)
// {
// 1: {"id": 1, "name": "Alice"},
// 2: {"id": 2, "name": "Bob"}
// }
Map
を作成したらMap.prototype.values()でvalueのみを抽出して、Array.from()で配列に変換したら終了です。
Map.prototype.values()
はIterator
オブジェクトを返すのでArray.from()
で変換が必要です。
filterを使わない理由
以下のような方法でも重複を削除することが可能です。
findIndex
で最初のインデックスを取得してfilter
する(1個目だけ採用して重複を削除する)シンプルな書き方です。
標準的な関数を使っているので読みやすいかもしれません。
const uniqueUsers = users.filter(
(element, index, self) => self.findIndex((e) => e.id === element.id) === index
);
「filter
を使わずにMap
を使いましょう。」と言いましたが、実は、「要素数が小さい配列を」「頻繁に(多数)処理する」という前提がある場合には、filter
を使ったほうが早いです。(詳細は以下の記事を参考にしてください。)
個人的にはデータ量が増えたときにも対応できるのでMap
で書いておいたほうがいいかなと思ってます。
まとめ
Map
を使ってオブジェクトの配列の重複を削除する方法を紹介しました。
タイトル詐欺みたいですが、処理するデータ量が少ないときはfilter
のほうがいいです。
でも、個人的にはデータ量が増えたときのためにMap
で書いておいたほうがいいかなと思ってます。
なにかのお役に立てれば幸いです。