【TC39 Proposals】New Set methods

# 注意書き

2019 年 10 月 9 日に開催の [#tc39_study](https://web-study.connpass.com/event/147538/) 用に作成した資料です。
執筆時点で TC39 の Proposals に上がっている機能を紹介します。
将来的に JavaScript（ECMAScript）に取り入れられるかもしれませんし、取り入れられないかもしれませんし、紹介内容から仕様が大きく変更になるかもしれません。
TC39 の承認プロセスについては、https://tc39.es/process-document/ をご覧ください。

---

# New Set methods

現状の [Set](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Set) オブジェクトには、要素の追加・削除、要素が集合に含まれているかの基本的な操作しか提供されていません。
この Proposal は Set オブジェクトに集合操作のメソッドを追加するものです。
[他の言語](https://github.com/tc39/proposal-set-methods/blob/master/other-languages.md)や Immutable.js などのライブラリで使われているものと同等の機能を標準に取り入れます。
執筆時点で Stage 2 です。

---

# `Set.prototype.intersection()`

集合 $A$ と集合 $B$ の積集合 $A \cap B$ を返します。

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5])
console.log(a.intersection(b))
// -> Set { 3, 4 }
```

## 同等の処理

```javascript
const c = new Set()
for (const e of b) {
  if (a.has(e)) {
    c.add(e)
  }
}
console.log(c)
// -> Set { 3, 4 }
```

---

# `Set.prototype.union()`

集合 $A$ と集合 $B$ の和集合 $A \cup B$ を返します。

## 使い方

```javascript
// 基本的な使い方
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5])
console.log(a.union(b))
// -> Set { 1, 2, 3, 4, 5 }
```

## 同等の処理

```javascript
const c = new Set()
for (const e of a) {
  c.add(e)
}
for (const e of b) {
  c.add(e)
}
console.log(c)
```

---

# `Set.prototype.difference()`

集合 $A$ と集合 $B$ の差集合 $A \setminus B$ を返します。
※ A に含まれていない B の要素があっても OK

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5])
console.log(a.difference(b))
// -> Set { 1, 2 }
```

## 同等の処理

```javascript
const c = new Set()
for (const e of a) {
  if (!b.has(e)) {
    c.add(e)
  }
}
console.log(c)
// -> Set { 1, 2 }
```

---

# `Set.prototype.symmetricDifference()`

集合 $A$ と集合 $B$ の対称差集合 $(A \cup B) \setminus (A \cap B)$ を返します。

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5])
console.log(a.symmetricDifference(b))
// -> Set { 1, 2, 5 }
```

## 同等の処理

```javascript
const c = new Set()
for (const e of a) {
  c.add(e)
}
for (const e of b) {
  c.add(e)
}
for (const e of c) {
  if (a.has(e) && b.has(e)) {
    c.delete(e)
  }
}
console.log(c)
// -> Set { 1, 2, 5 }
```

---

# `Set.prototype.isSubsetOf()`

集合 $A$ と集合 $B$ について、$A \subseteq B$ の真偽を返します。

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([1, 2, 3, 4, 5])
const c = new Set([2, 3])
const d = new Set([4, 5, 6])
const e = new Set([6, 7, 8])
const f = new Set([1, 2, 3, 4])
const g = new Set()
console.log(a.isSubsetOf(b))
// -> true
console.log(a.isSubsetOf(c))
// -> false
console.log(a.isSubsetOf(d))
// -> false
console.log(a.isSubsetOf(e))
// -> false
console.log(a.isSubsetOf(f))
// -> true
console.log(a.isSubsetOf(g))
// -> false
```

---

# `Set.prototype.isDisjointFrom()`

集合 $A$ と集合 $B$ について、$A \cap B = \Phi$ の真偽を返します。

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([1, 2, 3, 4, 5])
const c = new Set([2, 3])
const d = new Set([4, 5, 6])
const e = new Set([6, 7, 8])
const f = new Set([1, 2, 3, 4])
const g = new Set()
console.log(a.isDisjointFrom(b))
// -> false
console.log(a.isDisjointFrom(c))
// -> false
console.log(a.isDisjointFrom(d))
// -> false
console.log(a.isDisjointFrom(e))
// -> true
console.log(a.isDisjointFrom(f))
// -> false
console.log(a.isDisjointFrom(g))
// -> true
```

---

# `Set.prototype.isSupersetOf()`

集合 $A$ と集合 $B$ について、$A \supseteq B$ の真偽を返します。

## 使い方

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([1, 2, 3, 4, 5])
const c = new Set([2, 3])
const d = new Set([4, 5, 6])
const e = new Set([6, 7, 8])
const f = new Set([1, 2, 3, 4])
const g = new Set()
console.log(a.isSupersetOf(b))
// -> false
console.log(a.isSupersetOf(c))
// -> true
console.log(a.isSupersetOf(d))
// -> false
console.log(a.isSupersetOf(e))
// -> false
console.log(a.isSupersetOf(f))
// -> true
console.log(a.isSupersetOf(g))
// -> true
```

---

# 共通事項

いずれのメソッドも元オブジェクトを変更しません。
また、引数側は Set オブジェクトである必要がなく、iterable で OK です。

```javascript
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5])
console.log(a.intersection(b))
// -> Set { 3, 4 }

// 元オブジェクトを変更しない
console.log(a, b)
// -> Set { 1, 2, 3, 4 } Set { 3, 4, 5 }

// 引数はSetじゃなくても良い
console.log(a.intersection([1, 3, 5]))
// -> Set { 1, 3 }
```

---

# 検討事項

クラスメソッド `Set.union(...iterables)` と `Set.intersection(...iterables)` は Proposal に含まれていませんが、検討の余地があるとされています。
Immutable.js にはこれらのクラスメソッドが含まれています。

---

# Polyfill

`core-js/proposals/set-methods` を読み込んでください。

CommonJS

```javascript
require('core-js/proposals/set-methods')
```

ES Modules

```javascript
import 'core-js/proposals/set-methods'
```

---

# 所感

これらの集合操作を自前で書くことが多かったので結構嬉しい。

---

# 参考情報

- https://github.com/tc39/set-methods
- https://github.com/likr-sandbox/tc39-study/tree/master/proposal-set-methods