ES6以前は、JavaScriptには集合のためのデータ構造がありませんでした。代わりに、2つの回避策が用いられていました。
ES6以降、JavaScriptは`Set`というデータ構造を持ち、任意の値を含み、メンバーシップチェックを高速に実行できます。
集合を作成するには、3つの一般的な方法があります。
まず、パラメータなしでコンストラクタを使用して、空の集合を作成できます。
const emptySet = new Set();
.equal(emptySet.size, 0); assert
次に、反復可能オブジェクト(例:配列)をコンストラクタに渡すことができます。反復された値は、新しい集合の要素になります。
const set = new Set(['red', 'green', 'blue']);
3番目に、`.add()`メソッドは集合に要素を追加し、連結可能です。
const set = new Set()
.add('red')
.add('green')
.add('blue');
`.add()`は集合に要素を追加します。
const set = new Set();
.add('red'); set
`.has()`は、要素が集合のメンバーであるかどうかを確認します。
.equal(set.has('red'), true); assert
`.delete()`は集合から要素を削除します。
.equal(set.delete('red'), true); // there was a deletion
assert.equal(set.has('red'), false); assert
`.size`は集合の要素数を保持します。
const set = new Set()
.add('foo')
.add('bar');
.equal(set.size, 2) assert
`.clear()`は集合のすべての要素を削除します。
.clear();
set.equal(set.size, 0) assert
集合は反復可能であり、`for-of`ループは期待どおりに機能します。
const set = new Set(['red', 'green', 'blue']);
for (const x of set) {
console.log(x);
}// Output:
// 'red'
// 'green'
// 'blue'
ご覧のとおり、集合は*挿入順序*を保持します。つまり、要素は常に追加された順序で反復処理されます。
集合が反復可能であることを考えると、`Array.from()`を使用して配列に変換できます。
const set = new Set(['red', 'green', 'blue']);
const arr = Array.from(set); // ['red', 'green', 'blue']
配列を集合に変換して戻すと、配列から重複が削除されます。
.deepEqual(
assertArray.from(new Set([1, 2, 1, 2, 3, 3, 3])),
1, 2, 3]); [
文字列は反復可能であるため、`new Set()`のパラメータとして使用できます。
.deepEqual(
assertnew Set('abc'),
new Set(['a', 'b', 'c']));
Mapキーと同様に、集合要素は`===`と同様に比較されますが、`NaN`は自身と等しいという例外があります。
> const set = new Set([NaN, NaN, NaN]);
> set.size1
> set.has(NaN)true
`===`と同様に、2つの異なるオブジェクトは決して等しいとはみなされません(そして、現時点ではそれを変更する方法はありません)。
> const set = new Set();
> set.add({});
> set.size1
> set.add({});
> set.size2
集合には、いくつかの一般的な演算がありません。このような演算は、通常、以下のように実装できます。
2つの集合`a`と`b`の和集合を計算するということは、`a`と`b`の両方の要素を含む集合を作成することを意味します。
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
// Use spreading to concatenate two iterables
const union = new Set([...a, ...b]);
.deepEqual(Array.from(union), [1, 2, 3, 4]); assert
2つの集合`a`と`b`の積集合を計算するということは、`a`の要素で`b`にも含まれる要素を含む集合を作成することを意味します。
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const intersection = new Set(
Array.from(a).filter(x => b.has(x))
;
)
.deepEqual(
assertArray.from(intersection), [2, 3]
; )
2つの集合`a`と`b`の差集合を計算するということは、`a`の要素で`b`に含まれない要素を含む集合を作成することを意味します。この演算は、*マイナス*(−)とも呼ばれます。
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const difference = new Set(
Array.from(a).filter(x => !b.has(x))
;
)
.deepEqual(
assertArray.from(difference), [1]
; )
集合には`.map()`メソッドがありません。しかし、配列のメソッドを借用することができます。
const set = new Set([1, 2, 3]);
const mappedSet = new Set(
Array.from(set).map(x => x * 2)
;
)
// Convert mappedSet to an Array to check what’s inside it
.deepEqual(
assertArray.from(mappedSet), [2, 4, 6]
; )
集合を直接`.filter()`することはできないため、対応する配列メソッドを使用する必要があります。
const set = new Set([1, 2, 3, 4, 5]);
const filteredSet = new Set(
Array.from(set).filter(x => (x % 2) === 0)
;
)
.deepEqual(
assertArray.from(filteredSet), [2, 4]
; )
`new Set<T>(values?: Iterable<T>)` [ES6]
パラメータ`values`を指定しない場合、空の集合が作成されます。指定した場合、反復された値が集合に要素として追加されます。例えば、
const set = new Set(['red', 'green', 'blue']);
`.add(value: T): this` [ES6]
`value`をこの集合に追加します。このメソッドは`this`を返すため、連結可能です。
const set = new Set(['red']);
.add('green').add('blue');
set.deepEqual(
assertArray.from(set), ['red', 'green', 'blue']
; )
`.delete(value: T): boolean` [ES6]
`value`をこの集合から削除します。何かが削除された場合は`true`を、そうでない場合は`false`を返します。
const set = new Set(['red', 'green', 'blue']);
.equal(set.delete('red'), true); // there was a deletion
assert.deepEqual(
assertArray.from(set), ['green', 'blue']
; )
`.has(value: T): boolean` [ES6]
`value`がこの集合に含まれているかどうかを確認します。
const set = new Set(['red', 'green']);
.equal(set.has('red'), true);
assert.equal(set.has('blue'), false); assert
`get .size: number` [ES6]
この集合にいくつの要素が含まれているかを返します。
const set = new Set(['red', 'green', 'blue']);
.equal(set.size, 3); assert
`.clear(): void` [ES6]
この集合からすべての要素を削除します。
const set = new Set(['red', 'green', 'blue']);
.equal(set.size, 3);
assert.clear();
set.equal(set.size, 0); assert
`.values(): Iterable<T>` [ES6]
この集合のすべての要素を反復処理する反復可能オブジェクトを返します。
const set = new Set(['red', 'green']);
for (const x of set.values()) {
console.log(x);
}// Output:
// 'red'
// 'green'
`[Symbol.iterator](): Iterable<T>` [ES6]
集合を反復処理するデフォルトの方法です。`.values()`と同じです。
const set = new Set(['red', 'green']);
for (const x of set) {
console.log(x);
}// Output:
// 'red'
// 'green'
`.forEach(callback: (value: T, key: T, theSet: Set<T>) => void, thisArg?: any): void` [ES6]
この集合の各要素を`callback()`に渡します。`value`と`key`はどちらも現在の要素を含みます。この冗長性は、この`callback`が`Map.prototype.forEach()`の`callback`と同じ型シグネチャを持つようにするために導入されました。
`thisArg`を使用して、`callback`の`this`を指定できます。省略した場合、`this`は`undefined`です。
const set = new Set(['red', 'green']);
.forEach(x => console.log(x));
set// Output:
// 'red'
// 'green'
以下の2つのメソッドは、主に集合とMapが同様のインターフェースを持つようにするために存在します。各集合要素は、キーと値が両方とも要素であるMapエントリとして扱われます。
`.entries()`を使用すると、集合をMapに変換できます。
const set = new Set(['a', 'b', 'c']);
const map = new Map(set.entries());
.deepEqual(
assertArray.from(map.entries()),
'a','a'], ['b','b'], ['c','c']]
[[; )
この質問の回答は、§33.6.4「なぜマップは`.size`を持ち、配列は`.length`を持つのか?」に記載されています。
クイズ
クイズアプリをご覧ください。