Set で配列の重複を取り除く

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

Set で配列の重複を取り除く とは

Set とスプレッド構文の組み合わせで、配列のユニーク化を 1 行で書く実用パターンを学ぶ。本レッスンでは、Set で配列の重複を取り除く の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

Set で配列の重複を取り除く

ログから一意なユーザー ID を抽出する、タグ一覧から重複を消す、フォーム入力の選択肢を整理する、というように「配列から重複を取り除く」処理は頻出です。JavaScriptSet を使えば、わずか 1 行で実装できます。for ループや indexOf で頑張る時代は終わりました。

[...new Set(arr)]重複排除の決まり文句。短く、速く、読みやすい。

Set とは?

Set重複を許さない値のコレクション です。new Set(iterable) で初期化でき、配列を渡すと自動で重複が消えます。順序は挿入順で保持されます。

JavaScript

const s = new Set([1, 2, 2, 3, 3, 3]); console.log(s.size); // 3 console.log([...s]); // [1, 2, 3]

Setiterable なので、スプレッド構文や for...of で展開できます。これと組み合わせるのが定番パターンです。

重複排除の定番パターン

JavaScript

const arr = [1, 2, 2, 3, 3, 3]; const unique = [...new Set(arr)]; console.log(unique); // [1, 2, 3]

たった 1 行です。Array.from(new Set(arr)) でも同じ動きですが、スプレッド版の方がリテラルで完結するので人気です。

JavaScript

// Array.from 版 const u1 = Array.from(new Set(arr)); // スプレッド版 const u2 = [...new Set(arr)];

どちらの書き方も覚えておく価値あり。Array.from には mapFn を渡せるので、変換と組み合わせると強い。

文字列配列でも同じ

値の型は数値でも文字列でも、混在していても動きます。比較は SameValueZero アルゴリズム で行われ、NaN も等しいとみなされます。

JavaScript

const tags = ["js", "react", "js", "node", "react"]; const uniq = [...new Set(tags)]; console.log(uniq); // ["js", "react", "node"]

動きを追ってみる

diagram (will load when visible)

図のとおり、Set のコンストラクタが重複を取り除き、スプレッドで配列に戻すという 2 ステップで完了します。

filter + indexOf の旧パターン

Set が登場する前は、filterindexOf で重複排除を書いていました。

JavaScript

const unique = arr.filter((v, i) => arr.indexOf(v) === i);

動きはしますが、indexOf が要素ごとに線形探索するので O(n^2) の計算量です。Set 版は O(n) なので、大きな配列では桁違いに速いです。新規コードでは Set 版を使いましょう。

速度 + 可読性」 の両方で Set 版が勝つ。古いコードを見つけたら置き換えのチャンス。

オブジェクトの重複は別問題

Set=== 相当の比較 をします。オブジェクトは参照比較なので、同じ形でも別オブジェクトなら重複扱いになりません。

JavaScript

const arr = [{ id: 1 }, { id: 1 }]; console.log([...new Set(arr)].length); // 2 (両方残る)

オブジェクト配列を一意化したいときは、キー (例えば id) ベースMapreduce を使います。

JavaScript

const byId = [...new Map(arr.map((o) => [o.id, o])).values()]; console.log(byId.length); // 1

よくある間違い

第 1 に、Set をそのまま返してしまい、UI 側で .length が読めず慌てるパターンがあります。Set には size プロパティはありますが length はありません。配列に戻すのを忘れずに。第 2 に、ネストした配列やオブジェクトを Set で一意化しても 参照比較 のため期待通りに動きません。第 3 に、NaN を含めると indexOf 版は 2 個残しますが、Set 版は 1 個に集約します。挙動の違いに注意してください。

やってみよう

数値配列 arr を受け取り、重複を取り除いた新配列を返す unique 関数を書きましょう。return [...new Set(arr)] の 1 行で完成です。順序は元配列の最初の出現順に保たれます。文字列配列でも同じコードがそのまま動くので、tagsslug の整理にも応用できます。慣れたら Sethasadddelete メソッドも触ってみてください。

よくある質問

Q. set とリストはどう違いますか?

A. set は重複を持たない集合で、要素の追加と存在確認が O(1) と高速です。リストは順序付きで重複を許し、要素の前後関係が意味を持つ場面で使います。重複除去や「含まれているか」判定の高速化が必要なら set が圧倒的に有利です。

Q. set の順序は保証されますか?

A. set は順序が保証されません。順序を保ったまま重複を除去したい場合は dict.fromkeys(items) を使う(Python)か、Set + 配列の組み合わせで管理してください。順序付きで集合演算したい場面は意外と多いので覚えておくと便利です。

Q. set 同士の積集合・和集合はどう書きますか?

A. a & b で積集合、a | b で和集合、a - b で差集合になります。リスト同士の共通要素を求めたいときは set(a) & set(b) で O(n) に高速化できます。文字列もイテラブルなので set('abc') & set('bcd') → {'b', 'c'} のように使えます。

次のレッスン

次は Object.keys と Object.values で集計する で、Set とスプレッド構文の組み合わせで、配列のユニーク化を 1 行で書く実用パターンを学ぶ を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 重複排除 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 重複排除 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. Set を使うこと
  2. スプレッド構文または Array.from で配列に戻すこと
  3. 最初の出現順を保つこと

入出力例

test-cases.txt

unique([1,2,2,3,3,3])[1,2,3] unique([3,1,2,1,3])[3,1,2] unique([1,2,3])[1,2,3] unique([])[]

ヒント

main.js
main.js
学習モード

メモ

Set で配列の重複を取り除く

⌘S で保存