ウェブエンジニア問題集

Map・Set・WeakRef — コレクション型の使いどころ

JavaScriptには、オブジェクトや配列とは別に MapSet という組み込みのコレクション型があります。 ES2015で追加されたこれらは、「キーの柔軟さ」や「値の一意性」といった、 従来のオブジェクト・配列では扱いにくかった要件をシンプルに解決します。

この章ではMap・Set・WeakMap・WeakSet・WeakRefの順に、それぞれの特性と実務での使いどころを整理します。

学習者学習者

オブジェクトがあるのに Map って必要なの?Set も配列で代用できそうだけど…何が違うの?

Map — あらゆる型をキーにできる連想配列

このセクションは現在執筆中です。

Set — 重複しない値のコレクション

Setは 同じ値を1つしか持たない コレクションです。 配列と似ていますが、インデックスによるアクセスはなく、「この値が含まれているか」を高速に判定できるのが特徴です。

基本の使い方

const s = new Set();
s.add(1);
s.add(2);
s.add(1); // すでに存在するので無視される(エラーにはならない)
 
console.log(s); // Set(2) { 1, 2 }
console.log(s.size); // 2

コンストラクタに配列を渡すと、重複を取り除いた状態で初期化されます。

const sample = new Set([1, 1, 2, 3, 3]);
console.log(sample); // Set(3) { 1, 2, 3 }

注意点として、console.log の出力は配列 [1, 2, 3] ではなく Set(3) { 1, 2, 3 } です。 配列として取り出したい場合はスプレッド構文か Array.from を使います。

const arr = [...sample]; // [1, 2, 3]
const arr2 = Array.from(sample); // [1, 2, 3]

重複の判定ルール — 「同じ値」とは何か

Setの同値判定は SameValueZero アルゴリズムに従います。 ほぼ ===(厳密等価)と同じですが、1つだけ違いがあります

// === での比較
console.log(NaN === NaN); // false(!)
 
// Setでの比較
const s = new Set();
s.add(NaN);
s.add(NaN);
console.log(s.size); // 1 — NaN同士を「同じ値」とみなす

=== では NaN は自分自身と等しくないという仕様ですが、 Setでは NaN を同一の値として扱います。 これは実用上は自然な挙動で、「NaNが際限なく追加されてしまう」といった事故を防ぎます。

もう1つ押さえておきたいのは、オブジェクトは参照で比較される という点です。

const s = new Set();
s.add({ id: 1 });
s.add({ id: 1 });
console.log(s.size); // 2 — 見た目が同じでも別オブジェクトなので重複扱いにならない

見た目が同じオブジェクトでも、=== で等しくなければ別の値として追加されます。

主要メソッド

メソッド説明戻り値
add(value)値を追加(重複なら無視)Set自身
has(value)値が存在するかboolean
delete(value)値を削除削除できたら true
clear()全要素を削除undefined
forEach(fn)各要素に対してコールバック実行undefined

add はSet自身を返すので、メソッドチェーンが可能です。

const s = new Set().add(1).add(2).add(3);

実務でよく使うパターン — 配列の重複排除

const ids = [1, 3, 5, 3, 1, 7];
const unique = [...new Set(ids)]; // [1, 3, 5, 7]

この1行イディオムは頻出です。APIレスポンスに重複IDが含まれる場合や、 ユーザーの選択肢から重複を取り除くときなどに使います。

Setはイテラブル

for...of やスプレッド構文で回せます。挿入順が保持されます。

const s = new Set(['a', 'b', 'c']);
 
for (const v of s) {
  console.log(v); // "a", "b", "c"
}

Setの集合演算(ES2025)

ES2025で、数学的な集合演算メソッドが追加されました。

const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
 
a.union(b); // Set { 1, 2, 3, 4 } — 和集合
a.intersection(b); // Set { 2, 3 }       — 積集合(共通要素)
a.difference(b); // Set { 1 }          — 差集合(aにだけある要素)
a.symmetricDifference(b); // Set { 1, 4 } — 対称差(どちらか片方にだけある要素)
a.isSubsetOf(b); // false — aはbの部分集合か
a.isSupersetOf(b); // false — aはbの上位集合か
a.isDisjointFrom(b); // false — 共通要素がないか

以前はスプレッドと filter で手書きしていた処理が、ネイティブメソッドで書けるようになりました。

WeakMap・WeakSet — GCに優しいコレクション

このセクションは現在執筆中です。

WeakRef・FinalizationRegistry — 弱参照と後始末

このセクションは現在執筆中です。

ちゃんと使うためのポイント

  • Setは同じ値を追加してもエラーにならず、単に無視される。サイレントに重複を弾く設計
  • 同値判定は === とほぼ同じだが、NaN 同士は同一とみなされる(SameValueZero)
  • オブジェクトは参照比較。見た目が同じでも === で等しくなければ別の値
  • 配列の重複排除は [...new Set(arr)] のワンライナーが定番
  • Mapはキーにオブジェクトを使いたいとき、Setは一意性を保証したいときに選ぶ

次の章では、for...ofの裏側にあるイテレータとジェネレータの仕組みを解説します。