Set

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

Set オブジェクトは、プリミティブ値やオブジェクト参照を問わず、あらゆる型で多数の一意の値を格納することができます。

解説

Set オブジェクトは値のコレクションです。 Set重複する値は格納出来ませんSet 内の値はコレクション内で一意になります。 Set はその要素について挿入順で反復処理を行うことができます。挿入順は、各要素が add メソッドによって正常に Set に挿入された順番に対応します。

仕様書では Set の実装について「平均アクセス時間がコレクション内の要素数に対して線形探索以下」であることが要求されています。したがって、計算量が O(N) よりも優れている限り、内部的にはハッシュ表(ルックアップは O(1))、探索木(ルックアップは O(log(N)))、または他のデータ構造として表すことができます。

値の等価性

値の等値性は、 SameValueZero アルゴリズムに基づいています。(以前は SameValue を使用しており、0 と -0 は異なるものとして扱われていました。詳しくはブラウザーの互換性の "Key equality for -0 and 0" を参照してください。)つまり、 NaNNaN と同じとみなされ(例え NaN !== NaN であっても)、それ以外の値は === 演算子の挙動に従って等しいとみなされます。

パフォーマンス

has メソッドは、値が Set 内にあるかどうかをチェックします。これは、以前に Set に追加された要素のほとんどを確認するよりも平均すると高速なアプローチを使用します。特に、 Array オブジェクトの lengthSet オブジェクトの size と等しい場合、平均して Array.prototype.includes メソッドより速くなります。

Set 風のブラウザー API

ブラウザーの Set 風オブジェクト(または「集合風オブジェクト」)とは、ウェブ API の中で多くの点で Set のように動作するインターフェイスです。

Set のように、要素はオブジェクトに追加した順番に反復処理することができます。 Set 風オブジェクトと Set には、名前と動作が同じプロパティとメソッドもあります。 しかし Set とは異なり、各アイテムには特定の定義済みの型しか使用できません。

許可されている型には仕様書の IDL 定義に設定されています。 例えば、 GPUSupportedFeaturesSet 風オブジェクトですが、キーと値として文字列を使用する必要があります。 これは下記の仕様 IDL で定義されています。

webidl
interface GPUSupportedFeatures {
  readonly setlike<DOMString>;
};

Set 風オブジェクトは、読み取り専用か読み書き可能かのどちらかです(上記の IDL で readonly キーワードを確認してください。

メソッドとプロパティは Set の等価なエンティティと同じ動作をしますが、項目のの型に制限があります。

以下のものが、読み取り専用の Set 風ブラウザーオブジェクトの例です。

以下のものが、読み書き可能な Set 風ブラウザーオブジェクトの例です。

コンストラクター

Set()

新しい Set オブジェクトを生成します。

静的プロパティ

Set[@@species]

派生オブジェクトを生成するために使用されるコンストラクター関数です。

インスタンスプロパティ

これらのプロパティは Set.prototype で定義されており、すべての Set インスタンスから共有されます。

Set.prototype.constructor

このインスタンスオブジェクトを生成したコンストラクター関数です。 Set インスタンスの場合、初期値は Set コンストラクターです。

Set.prototype.size

Set オブジェクト内の値の数を返します。

Set.prototype[@@toStringTag]

@@toStringTag プロパティの初期値は文字列の "Set" です。 このプロパティは Object.prototype.toString() で利用されます。

インスタンスメソッド

Set.prototype.add()

Set オブジェクト内に同じ値を持つ要素がまだ存在しなかった場合、指定した値を持つ新しい要素を Set オブジェクトに挿入します。

Set.prototype.clear()

すべての要素を Set オブジェクトから取り除きます。

Set.prototype.delete()

value に関連した要素を取り除き、要素の削除に成功したかどうかを示す論理値を返します。 Set.prototype.has(value) はその後は false を返します。

Set.prototype.entries()

Set オブジェクト内の各要素を [value, value] の配列の形で挿入順で返す、新しいイテレーターオブジェクトを返します。これは Map オブジェクトと似ていますが、それぞれの項目の keySet では value と同じになります。

Set.prototype.forEach()

Set オブジェクト内に存在する各値に対して、挿入順に一度ずつ callbackFn を呼びます。thisArg 引数が指定された場合、それぞれの callbackFn の呼び出しにおいて this の値として使用されます。

Set.prototype.has()

Set オブジェクト内に引数で与えられた値をもつ要素が存在するかどうかを示す論理値を返します。

Set.prototype.keys()

Set.prototype.values() の別名です。

Set.prototype.values()

Set オブジェクト内の各要素のを挿入順に返す、新しいイテレーターオブジェクトを返します。

Set.prototype[@@iterator]()

Set オブジェクト内の各要素のを挿入順に返す、新しいイテレーターオブジェクトを返します。

Set オブジェクトの使用

js
const mySet1 = new Set();

mySet1.add(1); // Set(1) { 1 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add("some text"); // Set(3) { 1, 5, 'some text' }
const o = { a: 1, b: 2 };
mySet1.add(o);

mySet1.add({ a: 1, b: 2 }); // o は別なオブジェクトを参照しているので、これは良い

mySet1.has(1); // true
mySet1.has(3); // false。 3 はセットに追加されないため
mySet1.has(5); // true
mySet1.has(Math.sqrt(25)); // true
mySet1.has("Some Text".toLowerCase()); // true
mySet1.has(o); // true

mySet1.size; // 5

mySet1.delete(5); // セットから 5 を取り除く
mySet1.has(5); // false。 5 は削除された

mySet1.size; // 4。 1 つの値を削除したばかりであるため

mySet1.add(5); // Set(5) { 1, 'some text', {...}, {...}, 5 }。 一度削除された要素は、新しい要素として追加されるため削除前の位置は保持されない

console.log(mySet1); // Set(5) { 1, "some text", {…}, {…}, 5 }

Set の反復処理

挿入順に要素を訪問する集合に対する反復処理です。

js
for (const item of mySet1) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.keys()) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.values()) {
  console.log(item);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// ここではキーと値は同じ
for (const [key, value] of mySet1.entries()) {
  console.log(key);
}
// 1, "some text", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// Set オブジェクトを Array オブジェクトに Array.from で変換
const myArr = Array.from(mySet1); // [1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}, 5]

// HTML 文書内であれば以下のものも動作する
mySet1.add(document.body);
mySet1.has(document.querySelector("body")); // true

// Set と Array の間の変換
const mySet2 = new Set([1, 2, 3, 4]);
console.log(mySet2.size); // 4
console.log([...mySet2]); // [1, 2, 3, 4]

// 交差する様子をシミュレートすることができる
const intersection = new Set([...mySet1].filter((x) => mySet2.has(x)));

// 差をシミュレートすることができる
const difference = new Set([...mySet1].filter((x) => !mySet2.has(x)));

// セット内の項目を forEach() で反復処理
mySet2.forEach((value) => {
  console.log(value);
});
// 1
// 2
// 3
// 4

基本的な集合演算の実装

js
function isSuperset(set, subset) {
  for (const elem of subset) {
    if (!set.has(elem)) {
      return false;
    }
  }
  return true;
}

function union(setA, setB) {
  const _union = new Set(setA);
  for (const elem of setB) {
    _union.add(elem);
  }
  return _union;
}

function intersection(setA, setB) {
  const _intersection = new Set();
  for (const elem of setB) {
    if (setA.has(elem)) {
      _intersection.add(elem);
    }
  }
  return _intersection;
}

function symmetricDifference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    if (_difference.has(elem)) {
      _difference.delete(elem);
    } else {
      _difference.add(elem);
    }
  }
  return _difference;
}

function difference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    _difference.delete(elem);
  }
  return _difference;
}

// 例
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 3]);
const setC = new Set([3, 4, 5, 6]);

isSuperset(setA, setB); // returns true
union(setA, setC); // returns Set {1, 2, 3, 4, 5, 6}
intersection(setA, setC); // returns Set {3, 4}
symmetricDifference(setA, setC); // returns Set {1, 2, 5, 6}
difference(setA, setC); // returns Set {1, 2}

配列との関係

js
const myArray = ["value1", "value2", "value3"];

// 通常の Set コンストラクターを使用して、 Array を Set に変換
const mySet = new Set(myArray);

mySet.has("value1"); // returns true

// スプレッド演算子を使って、集合を配列に変換
console.log([...mySet]); // myArray と全く同じ Array が表示されます。

配列から重複した要素を取り除く

js
// 配列から重複する要素を取り除くために使用

const numbers = [2, 3, 4, 4, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 5, 32, 3, 4, 5];

console.log([...new Set(numbers)]);

// [2, 3, 4, 5, 6, 7, 32]

文字列との関係

js
const text = "India";

const mySet = new Set(text); // Set(5) {'I', 'n', 'd', 'i', 'a'}
mySet.size; // 5

// 大文字と小文字の区別して重複を削除
new Set("Firefox"); // Set(7) { "F", "i", "r", "e", "f", "o", "x" }
new Set("firefox"); // Set(6) { "f", "i", "r", "e", "o", "x" }

Set を使用して、リスト中の値の一意性を保証

js
const array = Array.from(document.querySelectorAll("[id]")).map((e) => e.id);

const set = new Set(array);
console.assert(set.size === array.length);

仕様書

Specification
ECMAScript Language Specification
# sec-set-objects

ブラウザーの互換性

BCD tables only load in the browser

関連情報