impatient programmersのためのJavaScript(ES2022版)
この本のサポートをお願いします:購入する または 寄付する
(広告です。ブロックしないでください。)

35 集合(`Set`)



ES6以前は、JavaScriptには集合のためのデータ構造がありませんでした。代わりに、2つの回避策が用いられていました。

ES6以降、JavaScriptは`Set`というデータ構造を持ち、任意の値を含み、メンバーシップチェックを高速に実行できます。

35.1 集合の使い方

35.1.1 集合の作成

集合を作成するには、3つの一般的な方法があります。

まず、パラメータなしでコンストラクタを使用して、空の集合を作成できます。

const emptySet = new Set();
assert.equal(emptySet.size, 0);

次に、反復可能オブジェクト(例:配列)をコンストラクタに渡すことができます。反復された値は、新しい集合の要素になります。

const set = new Set(['red', 'green', 'blue']);

3番目に、`.add()`メソッドは集合に要素を追加し、連結可能です。

const set = new Set()
.add('red')
.add('green')
.add('blue');

35.1.2 要素の追加、削除、メンバーシップの確認

`.add()`は集合に要素を追加します。

const set = new Set();
set.add('red');

`.has()`は、要素が集合のメンバーであるかどうかを確認します。

assert.equal(set.has('red'), true);

`.delete()`は集合から要素を削除します。

assert.equal(set.delete('red'), true); // there was a deletion
assert.equal(set.has('red'), false);

35.1.3 集合のサイズの確認とクリア

`.size`は集合の要素数を保持します。

const set = new Set()
  .add('foo')
  .add('bar');
assert.equal(set.size, 2)

`.clear()`は集合のすべての要素を削除します。

set.clear();
assert.equal(set.size, 0)

35.1.4 集合の反復処理

集合は反復可能であり、`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']

35.2 集合の使用例

35.2.1 配列から重複を削除する

配列を集合に変換して戻すと、配列から重複が削除されます。

assert.deepEqual(
  Array.from(new Set([1, 2, 1, 2, 3, 3, 3])),
  [1, 2, 3]);

35.2.2 Unicode文字(コードポイント)の集合を作成する

文字列は反復可能であるため、`new Set()`のパラメータとして使用できます。

assert.deepEqual(
  new Set('abc'),
  new Set(['a', 'b', 'c']));

35.3 どのような集合要素が等しいとみなされるか?

Mapキーと同様に、集合要素は`===`と同様に比較されますが、`NaN`は自身と等しいという例外があります。

> const set = new Set([NaN, NaN, NaN]);
> set.size
1
> set.has(NaN)
true

`===`と同様に、2つの異なるオブジェクトは決して等しいとはみなされません(そして、現時点ではそれを変更する方法はありません)。

> const set = new Set();

> set.add({});
> set.size
1

> set.add({});
> set.size
2

35.4 不足している集合演算

集合には、いくつかの一般的な演算がありません。このような演算は、通常、以下のように実装できます。

35.4.1 和集合(`a` ∪ `b`)

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]);

assert.deepEqual(Array.from(union), [1, 2, 3, 4]);

35.4.2 積集合(`a` ∩ `b`)

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))
);

assert.deepEqual(
  Array.from(intersection), [2, 3]
);

35.4.3 差集合(`a` \ `b`)

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))
);

assert.deepEqual(
  Array.from(difference), [1]
);

35.4.4 集合のマッピング

集合には`.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
assert.deepEqual(
  Array.from(mappedSet), [2, 4, 6]
);

35.4.5 集合のフィルタリング

集合を直接`.filter()`することはできないため、対応する配列メソッドを使用する必要があります。

const set = new Set([1, 2, 3, 4, 5]);
const filteredSet = new Set(
  Array.from(set).filter(x => (x % 2) === 0)
);

assert.deepEqual(
  Array.from(filteredSet), [2, 4]
);

35.5 クイックリファレンス:`Set<T>`

35.5.1 コンストラクタ

35.5.2 `Set<T>.prototype`:単一の集合要素

35.5.3 `Set<T>.prototype`:すべての集合要素

35.5.4 `Set<T>.prototype`:反復処理とループ

35.5.5 `Map`との対称性

以下の2つのメソッドは、主に集合とMapが同様のインターフェースを持つようにするために存在します。各集合要素は、キーと値が両方とも要素であるMapエントリとして扱われます。

`.entries()`を使用すると、集合をMapに変換できます。

const set = new Set(['a', 'b', 'c']);
const map = new Map(set.entries());
assert.deepEqual(
  Array.from(map.entries()),
  [['a','a'], ['b','b'], ['c','c']]
);

35.6 FAQ:集合

35.6.1 なぜ集合は`.size`を持ち、配列は`.length`を持つのか?

この質問の回答は、§33.6.4「なぜマップは`.size`を持ち、配列は`.length`を持つのか?」に記載されています。

  クイズ

クイズアプリをご覧ください。