JavaScriptの開発において、配列から重複した要素を取り除く作業は非常に頻繁に発生するタスクの一つです。
データのクレンジングやUIへの表示最適化など、さまざまな場面で「一意な値のみを取り出したい」というニーズがあります。
かつては複雑なループ処理を記述する必要がありましたが、現在のJavaScript(ECMAScript)では非常に簡潔かつ高速に記述する方法が整っています。
本記事では、2026年現在の開発現場で推奨される標準的な手法から、パフォーマンスを重視したテクニック、さらにはオブジェクト配列の重複削除まで詳しく解説します。
Setオブジェクトを使用した最も効率的な重複削除
モダンなJavaScript開発において、配列の重複を削除する最も一般的で推奨される方法は、Setオブジェクトを活用することです。
Setは「重複を許さない値の集合」を管理するためのデータ構造であり、これを利用することで極めてシンプルなコードでユニークな配列を作成できます。
Setを用いた手法は、記述が簡潔であるだけでなく、内部的な最適化によって大量のデータを処理する際も高速に動作するという大きなメリットがあります。
スプレッド構文とSetの組み合わせ
もっとも短く記述できるのは、スプレッド構文(...)とSetを組み合わせる方法です。
// 重複を含む配列
const numbers = [1, 2, 2, 3, 4, 4, 5];
// Setに変換してからスプレッド構文で配列に戻す
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers);
[1, 2, 3, 4, 5]
このコードでは、まずnew Set(numbers)によって配列がSetオブジェクトに変換され、その過程で重複が自動的に排除されます。
その後、[...]という記述によって、Setの中身を再び新しい配列として展開しています。
Array.fromを使用した手法
スプレッド構文の代わりに、Array.from()メソッドを使用することも可能です。
可読性を重視する場合や、古いブラウザ環境へのトランスパイル設定によっては、こちらの記述が好まれることもあります。
const colors = ["red", "blue", "red", "green", "blue"];
// Array.fromを使用してSetを配列に変換
const uniqueColors = Array.from(new Set(colors));
console.log(uniqueColors);
["red", "blue", "green"]
どちらの手法もパフォーマンス上の差はほとんどありませんが、チームのコーディング規約に合わせて選択するのが良いでしょう。
filterとindexOfを使用した伝統的な手法
Setが登場する前のJavaScriptでは、filter()メソッドとindexOf()メソッドを組み合わせて重複を削除するのが一般的でした。
この方法は「今見ている要素が、配列の中で最初に登場した位置(インデックス)と同じかどうか」を判定するロジックに基づいています。
基本原理を理解する上では非常に有用ですが、大規模な配列に対しては処理速度が低下するという欠点があります。
filterによる重複削除のロジック
具体的な実装コードは以下の通りです。
const data = [10, 20, 10, 30, 20, 40];
const uniqueData = data.filter((value, index, self) => {
// 現在の要素が最初に現れるインデックスと、現在のインデックスを比較
return self.indexOf(value) === index;
});
console.log(uniqueData);
[10, 20, 30, 40]
indexOfは配列の先頭から順に値を探すため、重複が多い大きな配列では「ループの中でループを回す」ような計算コストがかかります。
計算量はO(n²)となり、要素数が数万件を超えるようなケースではブラウザのフリーズを招く恐れがあるため注意が必要です。
オブジェクト配列における重複削除の戦略
実務で最も直面するのは、単純な数値や文字列ではなく「オブジェクトを要素に持つ配列」から重複を除去したいケースです。
JavaScriptのSetはオブジェクトの参照(メモリ上の位置)で重複を判断するため、内容が同じ別オブジェクトは重複として削除されません。
そのため、特定のプロパティ(例えばidやemailなど)をキーにして、独自にフィルタリング処理を実装する必要があります。
Mapを活用した効率的な重複削除
オブジェクト配列において特定のキーでユニーク化を行う場合、Mapオブジェクトを利用するのが2026年現在でも最もスマートな解決策です。
const users = [
{ id: 1, name: "Tanaka" },
{ id: 2, name: "Sato" },
{ id: 1, name: "Tanaka (Duplicate)" },
{ id: 3, name: "Suzuki" }
];
// idをキーにしてMapに格納(後から登場したもので上書きされる)
const userMap = new Map(users.map(user => [user.id, user]));
// Mapの値だけを取り出して配列に戻す
const uniqueUsers = Array.from(userMap.values());
console.log(uniqueUsers);
[
{ "id": 1, "name": "Tanaka (Duplicate)" },
{ "id": 2, "name": "Sato" },
{ "id": 3, "name": "Suzuki" }
]
この手法では、Mapの「キーが重複しない」という特性を利用しています。
もし「最初に登場した要素を優先したい」場合は、以下のようにhasメソッドでチェックを行うロジックを記述します。
最初に登場したオブジェクトを優先する場合
const seen = new Map();
const firstOccurrenceUsers = users.filter(user => {
if (seen.has(user.id)) {
return false;
}
seen.set(user.id, true);
return true;
});
このように用途に応じて「最新を保持するか」「最初を保持するか」を使い分けることが重要です。
パフォーマンス比較と手法の選び方
重複削除の手法を選択する際は、処理するデータ量と可読性のバランスを考慮する必要があります。
以下の表は、それぞれの主な手法の特性をまとめたものです。
| 手法 | 適したデータ | 時間計算量 | メリット |
|---|---|---|---|
| Set | プリミティブ値 | O(n) | 最も高速で簡潔 |
| Map (Key指定) | オブジェクト | O(n) | 特定のプロパティで比較可能 |
| filter + indexOf | プリミティブ値 | O(n²) | 外部メモリを使わない |
| reduce | 複雑な条件 | O(n)〜 | 柔軟なロジックを組める |
基本的には「迷ったらSetを使う」という方針で問題ありません。
ただし、Internet Explorerのような極めて古い環境をサポートしなければならない特殊なプロジェクト(2026年では稀ですが)では、ポリフィルを入れるかfilter方式を採用する検討が必要です。
高速化のためのさらなるコツ
さらなるパフォーマンス向上を求める場合、特に巨大なデータセット(数十万件以上)を扱う際は、ブラウザのメインスレッドをブロックしない工夫が必要です。
例えば、Web Workersを利用してバックグラウンドで重複削除処理を実行することで、ユーザーインターフェースのカクつきを防ぐことができます。
また、メモリ消費を抑えたい場合は、既存の配列を破壊的に変更するのではなく、ジェネレータを使用して必要な分だけユニークな値を生成するといった高度な手法も存在します。
大規模データでの検証例
10万件の数値配列に対してSetを使用した処理時間は、現代のPCであれば数ミリ秒から数十ミリ秒程度で完了します。
しかし、同じ処理をfilterとindexOfで行うと、数秒から数十秒かかることも珍しくありません。
「アルゴリズムの選択一つでUX(ユーザー体験)が劇的に変わる」という点は、常に意識しておくべきプログラミングの要諦です。
Lodashなどのライブラリ使用について
かつてはLodashの_.uniqや_.uniqByといった関数が多用されてきました。
しかし、標準のJavaScriptが進化し、SetやMapが定着した現在では、重複削除のためだけに外部ライブラリを導入するメリットは薄れています。
プロジェクトですでにLodashが導入されている場合は活用しても良いですが、新規プロジェクトであれば標準機能を優先して使用しましょう。
よくある落とし穴:NaNとundefinedの扱い
JavaScriptの配列には、数値以外の特殊な値が含まれることがあります。
Setオブジェクトは、NaNを自分自身と等しいと見なすため、配列内に複数のNaNが含まれていても一つに集約してくれます。
const mixedArray = [NaN, NaN, undefined, undefined, 1];
const uniqueMixed = [...new Set(mixedArray)];
console.log(uniqueMixed);
[NaN, undefined, 1]
これは直感に反しない挙動であり、開発者にとって非常に便利な仕様です。
一方で、indexOfを用いた手法ではNaNを正しく判定できず、重複が削除されないという罠があります。
特殊な値が含まれる可能性がある場合は、迷わずSetを選択することが安全なコードへの近道です。
まとめ
JavaScriptにおける配列の重複削除は、現代ではSetオブジェクトを使用するのがベストプラクティスです。
コードが簡潔になるだけでなく、パフォーマンス面でも非常に優れているため、基本的にはこの方法を第一選択にしましょう。
一方で、オブジェクトの配列を扱う場合は、Mapを利用して特定のプロパティをキーにした重複チェックを行う必要があります。
処理するデータの構造や規模に応じて、適切なメソッドを使い分けることが、高品質なコードを書くためのポイントとなります。
本記事で紹介した手法を活用して、より高速でメンテナンス性の高いJavaScriptプログラムを構築してください。
