JavaScript | ES6+ 文法:新データ構造 – Set

JavaScript JavaScript
スポンサーリンク

Set とは何か(まずイメージから)

Set は、ES6 で追加された
「重複を許さない値の集まり(コレクション)」 です。

  • 配列:同じ値を何回入れてもいい
  • Set:同じ値は 1 回しか入らない

というイメージを持つと分かりやすいです。

const s = new Set();

s.add(1);
s.add(2);
s.add(2); // 同じ値は無視される

console.log(s);        // Set(2) { 1, 2 }
console.log(s.size);   // 2
JavaScript

ここが重要です。
「同じものが入っても困る」「重複を自動で取り除きたい」 ときに、Set が本領を発揮します。
配列で「重複チェック」「重複削除」をゴリゴリ書くより、Set を使う方が圧倒的に素直です。

Set の基本操作(add / has / delete / clear)

Set の作り方と add での追加

const visited = new Set();

visited.add("東京");
visited.add("大阪");
visited.add("東京"); // 2回目は無視される

console.log(visited);      // Set(2) { '東京', '大阪' }
console.log(visited.size); // 2
JavaScript

add(value) で値を追加します。
同じ値を何度 add しても、Set の中には 1 個しか入りません。

has で存在チェック、delete で削除

console.log(visited.has("東京")); // true
console.log(visited.has("名古屋")); // false

visited.delete("大阪");
console.log(visited.has("大阪")); // false
JavaScript

has(value) で「その値が入っているか」をチェックできます。
delete(value) は指定した要素を削除します。
全削除したいときは clear() を使います。

visited.clear();
console.log(visited.size); // 0
JavaScript

size で要素数がすぐ分かる

const s = new Set();
s.add(1);
s.add(2);
console.log(s.size); // 2
JavaScript

配列の length と同じイメージで、Setsize で長さを取得します。

重複しないとはどういうことか(ここを深掘り)

原始値(数値・文字列など)の場合

const s = new Set();

s.add(1);
s.add(1);
s.add("1");
s.add("1");

console.log(s); // Set(2) { 1, '1' }
JavaScript

1(数値)と "1"(文字列)は別物なので、両方入ります。
しかし、1 を何回 add しても、Set の中には 1つだけです。

オブジェクトや配列は「同一参照かどうか」で判断される

const s = new Set();

const a = { id: 1 };
const b = { id: 1 }; // 中身は同じでも別オブジェクト

s.add(a);
s.add(b);

console.log(s.size); // 2
JavaScript

オブジェクトや配列の場合、
「中身が同じかどうか」ではなく、「同じもの(同じ参照)かどうか」で重複が判断されます。

const s = new Set();

const arr = [1, 2];
s.add(arr);
s.add(arr); // 同じ参照を2回 add

console.log(s.size); // 1
JavaScript

ここが重要です。
Set は「=== で同じと判断されるかどうか」で重複を判定する
原始値なら「値そのもの」、オブジェクトなら「同じもの(参照)」かどうかで決まります。

Set のイテレーション(for…of / forEach)

Set は for…of でそのまま回せる

const s = new Set(["東京", "大阪", "名古屋"]);

for (const city of s) {
  console.log(city);
}
// 東京
// 大阪
// 名古屋
JavaScript

Set は「挿入した順」で値を返してくれます。

forEach も使える

s.forEach((value) => {
  console.log("value:", value);
});
JavaScript

Map の forEach と違い、Set のコールバックは (value, value, set) という少し変わった引数構成ですが、
普通は value だけ見ておけば十分です。

s.forEach((value, sameValue, set) => {
  // sameValue は value と同じ
});
JavaScript

よくある用途と具体例

例1:配列から重複を取り除く(ユニークな要素だけ)

一番よく使うパターンです。

const nums = [1, 2, 2, 3, 3, 3, 4];

const unique = [...new Set(nums)];

console.log(unique); // [1, 2, 3, 4]
JavaScript

new Set(nums) で「重複を取り除いた Set」を作り、
[...] で配列に戻しています。

例2:すでに見たことがあるかどうかを高速にチェック

例えば、ログイン試行時に「すでに試したメールアドレスかどうか」をチェックするイメージ。

const triedEmails = new Set();

function tryLogin(email) {
  if (triedEmails.has(email)) {
    console.log("このメールアドレスはすでに試しました");
    return;
  }

  triedEmails.add(email);
  console.log("ログイン試行:", email);
  // 実際のログイン処理…
}

tryLogin("a@example.com");
tryLogin("b@example.com");
tryLogin("a@example.com");
// ログイン試行: a@example.com
// ログイン試行: b@example.com
// このメールアドレスはすでに試しました
JavaScript

配列で同じことをしようとすると、
includes で毎回探したり、重複したら push しないようにしたりと、コードがもっさりします。
Set は「重複を気にせず add して OK」というのがかなり楽です。

例3:2つの配列の共通要素(積集合)を求める

数学の集合のイメージです。

const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];

const setB = new Set(b);

const intersection = a.filter((x) => setB.has(x));

console.log(intersection); // [3, 4]
JavaScript

setB.has(x) が高速に判定できるので、
共通部分の計算も効率よく書けます。

例4:2つの配列を合わせてユニークな集合にする(和集合)

const a = [1, 2, 3];
const b = [3, 4, 5];

const union = [...new Set([...a, ...b])];

console.log(union); // [1, 2, 3, 4, 5]
JavaScript

Set と配列をどう使い分けるか

配列が向いている場面

配列は

  • 順番に意味がある(1番目、2番目…)
  • 重複を許容したい
  • インデックスでアクセスしたい(arr[0] など)

といったケースに向いています。

例えば、「ユーザーがクリックした履歴」などは配列が自然です。

const clicks = [];
clicks.push("top");
clicks.push("about");
clicks.push("top"); // top を複数回クリックしてもよい
JavaScript

Set が向いている場面

Set は

  • 値の重複を許したくない
  • 「この値が存在するかどうか」を高速に判定したい
  • 順序は必要だけど、インデックスでアクセスする必要はあまりない

といったケースに向いています。

例えば、「今ログインしているユーザーIDの集合」など。

const loggedInUserIds = new Set();

loggedInUserIds.add(1);
loggedInUserIds.add(2);
loggedInUserIds.add(1); // 重複しても 1 は 1 つだけ

console.log(loggedInUserIds.has(1)); // true
JavaScript

ここが重要です。
「順序付きのリスト + 重複OK」なら配列。
「順序付きの集合 + 重複NG」なら Set。

このイメージを持っておくと、自然に使い分けられます。

Set と Map / オブジェクトの関係

Set は「値だけ」、Map は「キーと値」

  • Set:値だけを持つ(各値は一意)
  • Map:キーと値のペアを持つ
const set = new Set(["A", "B", "C"]); // 値だけ

const map = new Map([
  ["A", 1],
  ["B", 2],
]); // キーと値
JavaScript

「何かの“存在”だけ管理したい」なら Set、
「キーに紐づく値を持ちたい」なら Map を選びます。

オブジェクトは「固定フィールド」、Set/Map は「可変な集合」

  • オブジェクト:あらかじめ決まった「項目」を持つデータ(id, name, age …)
  • Set/Map:増えたり減ったりする「集合」を扱うコレクション

この違いを意識すると、
「ここはオブジェクト」「ここは Set/Map」と切り分けやすくなります。

まとめ

Set の核心は、
「重複しない値の集合を、簡単かつ効率よく扱うためのデータ構造」 であることです。

押さえておきたいポイントは次の通りです。

  • add, has, delete, clear, size が基本操作
  • 同じ値を何度 add しても 1 回分しか入らない(重複が自動的に排除される)
  • オブジェクトや配列も要素として持てる(=== で同じかどうかで判定)
  • for...offorEach で順序通りにループできる
  • 「配列の重複を消す」「共通部分(積集合)や和集合を計算する」「存在チェック」などで特に威力を発揮する
  • 「重複OKのリスト」なら配列、「重複NGの集合」なら Set を選ぶとよい

まずは、配列で書いているコードの中から

  • 重複を消したい場面
  • includes で存在チェックしている場面

を一つ見つけて、それを Set に置き換えてみてください。
Set を一度「重複削除の必殺技」として体感すると、どこで使えば気持ちいいかがどんどん見えてきます。

タイトルとURLをコピーしました