## 32.集合(`Set`)
> 原文: [http://exploringjs.com/impatient-js/ch_sets.html](http://exploringjs.com/impatient-js/ch_sets.html)
在 ES6 之前,JavaScript 沒有集合的數據結構。相反,使用了兩種解決方法:
* 對象的鍵作為字符串集。
* 數組作為任意值的集合(例如,通過`.includes()`檢查元素是否在集合中),缺點是元素檢查緩慢。
ECMAScript 6 具有數據結構`Set`,適用于任意值且具有快速執行元素檢查。
### 32.1. 使用集合
#### 32.1.1. 創建集合
創建集合有三種常用方法。
首先,您可以使用不帶任何參數的構造函數來創建空集:
```js
const emptySet = new Set();
assert.equal(emptySet.size, 0);
```
其次,您可以將迭代(例如,數組)傳遞給構造函數。迭代值成為新集的元素:
```js
const set = new Set(['red', 'green', 'blue']);
```
第三,`.add()`方法將元素添加到 Set 并且是可鏈式調用的:
```js
const set = new Set()
.add('red')
.add('green')
.add('blue');
```
#### 32.1.2. 添加,刪除,檢查包含
`.add()`向 `Set` 添加元素。 `.has()`檢查某個元素是否包含在一個集合中。 `.delete()`從 `Set` 中刪除元素。
```js
const set = new Set();
set.add('red');
assert.equal(set.has('red'), true);
assert.equal(set.delete('red'), true); // there was a deletion
assert.equal(set.has('red'), false);
```
#### 32.1.3. 確定 Set 的大小和清除
`.size`包含 `Set` 中的元素數量。 `.clear()`刪除 `Set` 的所有元素。
```js
const set = new Set()
.add('foo')
.add('bar')
;
assert.equal(set.size, 2)
set.clear();
assert.equal(set.size, 0)
```
#### 32.1.4. 遍歷集合
集合是可迭代的,`for-of`循環可按預期工作:
```js
const set = new Set(['red', 'green', 'blue']);
for (const x of set) {
console.log(x);
}
// Output:
// 'red'
// 'green'
// 'blue'
```
如您所見,集合保留插入順序。也就是說,元素總是按照添加順序迭代。
集合是可迭代的,可以使用[展開(...)](ch_arrays.html#spreading-into-arrays)方法將其轉換為`Array`。
```js
const set = new Set(['red', 'green', 'blue']);
const arr = [...set]; // ['red', 'green', 'blue']
```
### 32.2. 使用`Set`示例
#### 32.2.1. 從數組中刪除重復項
將數組轉換為 Set 并返回,將從數組中刪除重復項:
```js
assert.deepEqual(
[...new Set([1, 2, 1, 2, 3, 3, 3])],
[1, 2, 3]);
```
#### 32.2.2 創建一個Unicode字符集合
字符串也是可迭代的,因此可以用作`new Set()`的參數:
```js
assert.deepEqual(
new Set('abc'),
new Set(['a', 'b', 'c']));
```
### 32.3. 什么集合元素被認為是相等的?
與 `Map`的key一樣,集合元素的比較類似于`===`,但`NaN`等于它自身。
```js
> const set = new Set([NaN, NaN, NaN]);
> set.size
1
> set.has(NaN)
true
```
第二次添加元素無效:
```js
> const set = new Set();
> set.add('foo');
> set.size
1
> set.add('foo');
> set.size
1
```
與`===`類似,兩個不同的對象永遠不會被認為是相同的(目前無法自定義):
```js
> const set = new Set();
> set.add({});
> set.size
1
> set.add({});
> set.size
2
```
### 32.4. 缺少的 Set 操作
集合缺少幾個常見操作。它們通常可通過以下方式實現:
* 將集轉換為數組(通過[展開(`...`)](ch_arrays.html#spreading-into-arrays))。
* 對數組執行操作。
* 將結果轉換回 Set。
#### 32.4.1. 并集(a∪b)
計算兩個集合a和b的并集是指創建一個新的集合,包含a和b的所有元素。
```js
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([...union], [1, 2, 3, 4]);
```
#### 32.4.2. 交集(a∩b)
計算兩個集合a和b的交集是指創建一個新的集合,包含同時包含在a和b中的元素。
```js
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const intersection = new Set(
[...a].filter(x => b.has(x)));
assert.deepEqual([...intersection], [2, 3]);
```
#### 32.4.3. 差集(a\b)
計算兩個集合a和b的差集是指創建一個新的集合,包含那些在a中而不在b中的元素。該操作又稱為減法(-)。
```js
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const difference = new Set(
[...a].filter(x => !b.has(x)));
assert.deepEqual([...difference], [1]);
```
#### 32.4.4. 集合映射
集合沒有`.map()`方法。但是我們可以從Array中借用。
```js
const set = new Set([1, 2, 3]);
const mappedSet = new Set([...set].map(x => x * 2));
// Convert mappedSet to an Array to check what’s inside it
assert.deepEqual([...mappedSet], [2, 4, 6]);
```
#### 32.4.5. 集合過濾
我們不能直接使用`.filter()`操作集合,因此我們需要使用Array的相應方法。
```js
const set = new Set([1, 2, 3, 4, 5]);
const filteredSet = new Set([...set].filter(x => (x % 2) === 0));
assert.deepEqual([...filteredSet], [2, 4]);
```
### 32.5. 快速參考:`Set<T>`
#### 32.5.1. 構造函數
* `new Set<T>(values?: Iterable<T>)` <sup>[ES6]</sup>
如果未提供參數`values`,則會創建一個空集。如果提供`values`,則迭代值將作為元素添加到 Set 中。例如:
```js
const set = new Set(['red', 'green', 'blue']);
```
#### 32.5.2. `Set<T>.prototype`:設置單個元素
* `.add(value: T): this` <sup>[ES6]</sup>
將`value`添加到此 Set。此方法返回`this`,這意味著它可以鏈式調用。
```js
const set = new Set(['red']);
set.add('green').add('blue');
assert.deepEqual([...set], ['red', 'green', 'blue']);
```
* `.delete(value: T): boolean` <sup>[ES6]</sup>
從此 Set 中刪除`value`,如果刪除成功返回`true`,反之返回`false`。
```js
const set = new Set(['red', 'green', 'blue']);
assert.equal(set.delete('red'), true); // there was a deletion
assert.deepEqual([...set], ['green', 'blue']);
```
* `.has(value: T): boolean` <sup>[ES6]</sup>
檢查`value`是否在此集合中。
```js
const set = new Set(['red', 'green']);
assert.equal(set.has('red'), true);
assert.equal(set.has('blue'), false);
```
#### 32.5.3. `Set<T>.prototype`:所有 Set 元素
* `get .size: number` <sup>[ES6]</sup>
返回此 Set 中有多少元素。
```js
const set = new Set(['red', 'green', 'blue']);
assert.equal(set.size, 3);
```
* `.clear(): void` <sup>[ES6]</sup>
從此 Set 中刪除所有元素。
```js
const set = new Set(['red', 'green', 'blue']);
assert.equal(set.size, 3);
set.clear();
assert.equal(set.size, 0);
```
#### 32.5.4. `Set<T>.prototype`:迭代和循環
* `.values(): Iterable<T>` <sup>[ES6]</sup>
返回包含該 Set 的所有元素的迭代器。
```js
const set = new Set(['red', 'green']);
for (const x of set.values()) {
console.log(x);
}
// Output:
// 'red'
// 'green'
```
* `[Symbol.iterator](): Iterable<T>` <sup>[ES6]</sup>
迭代 Set 的默認方式。與`.values()`相同。
```js
const set = new Set(['red', 'green']);
for (const x of set) {
console.log(x);
}
// Output:
// 'red'
// 'green'
```
* `.forEach(callback: (value: T, value2: T, theSet: Set<T>) => void, thisArg?: any): void` <sup>[ES6]</sup>
循環遍歷此 Set 的元素,并為每個元素調用回調(第一個參數)。 `value`和`key`都設置為當前元素,這種冗余設計是為了使該方法的回調與`Map.prototype.forEach`相同的類型簽名。
可以通過`thisArg`指定`this`的回調。否則,`this`設置為`undefined`。
```js
const set = new Set(['red', 'green']);
set.forEach(x => console.log(x));
// Output:
// 'red'
// 'green'
```
#### 32.5.5. 與`Map`對稱
僅存在以下兩種方法,以使 Sets 的接口類似于 Maps 的接口。處理每個 Set 元素就好像它是一個 Map 鍵,其鍵和值是元素。
* `Set.prototype.entries(): Iterable<[T,T]>` <sup>[ES6]</sup>
* `Set.prototype.keys(): Iterable<T>` <sup>[ES6]</sup>
`.entries()`使您可以將 Set 轉換為 Map:
```js
const set = new Set(['a', 'b', 'c']);
const map = new Map(set.entries());
assert.deepEqual(
[...map.entries()],
[['a','a'], ['b','b'], ['c','c']]);
```
 **測驗**
參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
- I.背景
- 1.關于本書(ES2019 版)
- 2.常見問題:本書
- 3. JavaScript 的歷史和演變
- 4.常見問題:JavaScript
- II.第一步
- 5.概覽
- 6.語法
- 7.在控制臺上打印信息(console.*)
- 8.斷言 API
- 9.測驗和練習入門
- III.變量和值
- 10.變量和賦值
- 11.值
- 12.運算符
- IV.原始值
- 13.非值undefined和null
- 14.布爾值
- 15.數字
- 16. Math
- 17. Unicode - 簡要介紹(高級)
- 18.字符串
- 19.使用模板字面值和標記模板
- 20.符號
- V.控制流和數據流
- 21.控制流語句
- 22.異常處理
- 23.可調用值
- VI.模塊化
- 24.模塊
- 25.單個對象
- 26.原型鏈和類
- 七.集合
- 27.同步迭代
- 28.數組(Array)
- 29.類型化數組:處理二進制數據(高級)
- 30.映射(Map)
- 31. WeakMaps(WeakMap)
- 32.集(Set)
- 33. WeakSets(WeakSet)
- 34.解構
- 35.同步生成器(高級)
- 八.異步
- 36. JavaScript 中的異步編程
- 37.異步編程的 Promise
- 38.異步函數
- IX.更多標準庫
- 39.正則表達式(RegExp)
- 40.日期(Date)
- 41.創建和解析 JSON(JSON)
- 42.其余章節在哪里?