Set で重複排除とは何か(まずイメージから)
Set は
「同じ値を 2 回入れようとしても、1 回分しか持たないコレクション」
でしたね。
この「同じ値は 1 つだけ」という性質を利用すると、
配列に同じ値が何個も入っていても
「かんたんに重複を取り除いた配列」に変換できます。
結論から言うと、こう書きます。
const nums = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(nums)];
console.log(unique); // [1, 2, 3, 4]
JavaScriptnew Set(nums) で「重複なし集合」にして、[...] でまた配列に戻している、という動きです。
ここが重要です。
「配列の重複を消したい」と思ったら、「for や if で頑張る前に Set を思い出す」。
これが ES6 以降の鉄板パターンです。
Set による重複排除の基本パターン
配列 → Set → 配列 の 3 ステップ
動きを分解するとこうなります。
- 配列から Set を作る
- Set の中で自動的に重複が消える
- Set を配列に戻す
コードで順番に書くと、こういう形です。
const nums = [1, 2, 2, 3, 3, 3, 4];
// 1. 配列から Set を作る(この時点で重複はない集合になる)
const set = new Set(nums);
// 2. Set をスプレッド構文で展開して、
// 3. [] の中に入れ直すことで「新しい配列」にする
const unique = [...set];
console.log(unique); // [1, 2, 3, 4]
JavaScript1 行で書くと、こう。
const unique = [...new Set(nums)];
JavaScript「Set は重複を許さない」
「スプレッド構文 ... は反復可能なものを展開する」
この 2 つを組み合わせた、ES6 らしいテクニックです。
文字列でも同じように使える
数値だけでなく、文字列にもそのまま使えます。
const names = ["Alice", "Bob", "Alice", "Charlie", "Bob"];
const uniqueNames = [...new Set(names)];
console.log(uniqueNames); // ["Alice", "Bob", "Charlie"]
JavaScript書き味を体で覚える
何度も繰り返しますが、形をそのまま覚えてしまって大丈夫です。
const unique = [...new Set(array)];
JavaScript「配列 → Set → 配列」という変換だと頭で理解しつつ、
指はこのワンライナーを書けるようにしておく、とても価値があります。
なぜ Set で重複が消えるのか(ここを深掘り)
Set は「同じ値を 2 回 add しても 1 回分だけ」持つ
Set を単体で使うと、こういう動きでした。
const s = new Set();
s.add(1);
s.add(2);
s.add(2); // 2 回目は無視される
s.add(3);
s.add(3);
s.add(3); // 全部無視
console.log(s); // Set(3) { 1, 2, 3 }
console.log(s.size); // 3
JavaScript「すでにある値なら、増えない」という性質が標準でついています。
この性質が、配列から Set を作ったときにもそのまま効きます。
const nums = [1, 2, 2, 3, 3, 3, 4];
const s = new Set(nums); // add を順番にやっているイメージ
console.log(s); // Set(4) { 1, 2, 3, 4 }
JavaScript何をもって「同じ」と判断しているか
Set は、内部的には ===(厳密等価)で見ていると考えてOKです。
数値や文字列などのプリミティブ値なら、「値そのもの」が同じなら同値とみなします。
const s = new Set();
s.add(1);
s.add(1); // 同じ数値 → 重複扱い
s.add("1"); // 文字列 "1" は別物
console.log(s); // Set(2) { 1, '1' }
JavaScriptオブジェクトや配列の場合は、「中身」ではなく「同じ参照かどうか」で判断します。
const a = { id: 1 };
const b = { id: 1 };
const s = new Set();
s.add(a);
s.add(b);
console.log(s.size); // 2(別オブジェクトなので両方入る)
JavaScriptここが重要です。
オブジェクトを Set で重複排除したいときは、「同じオブジェクトを 2 回入れたら重複として扱われる」のであって、「中身が同じ別オブジェクト」は別物として扱われる。
プリミティブ値の重複排除と、オブジェクトの重複排除は感覚が違うので注意してください。
Set を使わない書き方との比較(違いを体感する)
古い発想:for と includes で頑張る
Set がなかった頃、よくこう書いていました。
const nums = [1, 2, 2, 3, 3, 3, 4];
const unique = [];
for (const n of nums) {
if (!unique.includes(n)) {
unique.push(n);
}
}
console.log(unique); // [1, 2, 3, 4]
JavaScript分かりやすいですが、
unique.includesで毎回線形検索- 「含まれていなければ push」ロジックを毎回書く
と、処理もコードも少し重いです。
ES6 の発想:Set に丸投げ
同じことを Set で書くと、こうなります。
const nums = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(nums)];
JavaScriptコードが短くなるだけでなく、
「やりたいこと」が Set とワンライナーに集約されて、意図が伝わりやすくなっています。
ここが重要です。
Set を使うことで、自分のコードから「重複チェックのロジック」を追い出せる。
「重複を気にする」ところを Set に全部任せて、自分はロジックの本質だけを書く、という発想です。
応用:重複排除+他の変換を同時にやる
重複排除してからソートする
const nums = [3, 1, 2, 3, 2, 1, 5];
const uniqueSorted = [...new Set(nums)].sort((a, b) => a - b);
console.log(uniqueSorted); // [1, 2, 3, 5]
JavaScript「重複排除 → 昇順ソート」が 1 行で書けます。
重複排除してからフィルタする
const nums = [1, 2, 2, 3, 3, 3, 4, 5];
const uniqueEven = [...new Set(nums)].filter((n) => n % 2 === 0);
console.log(uniqueEven); // [2, 4]
JavaScriptオブジェクト配列の重複排除(キーを基準に)
オブジェクトそのものは Set で簡単に「中身の重複」を判定できないので、
「特定のキーの重複を避けたい」場合は Map や Set と組み合わせます。
例えば、「id が重複しているユーザーを 1 つにまとめたい」ケース。
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 1, name: "Alice2" },
];
const seenIds = new Set();
const uniqueUsers = [];
for (const user of users) {
if (seenIds.has(user.id)) continue;
seenIds.add(user.id);
uniqueUsers.push(user);
}
console.log(uniqueUsers);
// id が 1 のものは最初の 1 つだけ残る
JavaScriptここが重要です。
「オブジェクト配列の重複排除」は、直接 Set に配列を渡しても望んだ結果にならないことが多い。
「何をもって重複とみなすか?」(id?name?全フィールド?)をはっきりさせて、そのキーを Set に入れて判定します。
注意点と「ここまで知っていれば十分」なライン
Set で順番はどうなる?
new Set(array) でできる Set は、元の配列の「登場順」を保ちます。
const nums = [3, 1, 3, 2, 2, 1];
const unique = [...new Set(nums)];
console.log(unique); // [3, 1, 2]
JavaScript最初に出てきた 3 → 1 → 2 の順番が残っています。
順序が大事な場合も、Set を使って大丈夫です。
元の配列は変更されない(イミュータブルに扱える)
const nums = [1, 2, 2, 3];
const unique = [...new Set(nums)];
console.log(nums); // [1, 2, 2, 3]
console.log(unique); // [1, 2, 3]
JavaScript元の配列を壊さず、「重複を取った新しい配列」を作っているだけです。
関数型っぽく、安全に扱えます。
性能面でも素直に「Set に任せて OK」
重複数が多かったり、配列が大きくなったりしても、
Set はハッシュ構造を使って効率よく存在チェックをしてくれます。
「パフォーマンスが心配だから for で自前実装しよう」と悩む前に、
まずは Set でシンプルに書くのがおすすめです。
まとめ
Set による重複排除の本質は、
「Set の“同じ値は 1 つだけ”という性質を、配列に一瞬だけ借りる」 ことです。
押さえておきたいポイントをまとめると:
配列の重複排除の基本形は const unique = [...new Set(array)]
Set は同じ値を何度 add しても 1 回分しか持たない
プリミティブ値(数値・文字列など)の重複排除はこのワンライナーでほぼ解決できる
オブジェクト配列の重複排除では、「どのキーを基準に重複とみなすか」を決めて、そのキーを Set に入れて判定する
従来の「includes でチェックしてから push」より、Set の方が意図が明確でコードも短くなる
まずは、自分のコードの中で
「一度配列を作ったあとに、重複を消したくなっている場所」
を探して、その部分を
const unique = [...new Set(array)];
JavaScriptに置き換えてみてください。
「これだけでよかったんだ」という感覚が、一度でも腹落ちすれば、
Set による重複排除はもうあなたの武器になっています。
