(初心者向け) JavaScript の連想配列 (Map) と集合 (Set)

#概要

従来、JavaScript では連想配列はオブジェクトを利用して実現していました。しかし、ECMAScript6 (仕様) では、標準実装された Map (連想配列) と Set (集合) が定義されています。そして、これらはモダンブラウザ (FireFox や Chrome etc.) や Node.js 8.9.xLTS で利用可能になっています。

これまで通り、オブジェクトを連想配列として使用することもできますが、オブジェクトにはイテレータが実装されていないため、for of 構文を使用できません。


#Map の使用例

下のサンプルは Map オブジェクトを作成して、データを追加し、ちゃんと入っていることを確認しています。

```html
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
 <script>
  const testMap = () => {
    var m = new Map();
    m.set('a', 1);
    m.set('b', 2);
    m.set('c', 3);
    $('#map1').html(m.get('a') + m.get('c'));
  }

  $(document).ready(() => {
    testMap();
  });
 </script>
```

スクリプトの実行結果
4


#Set の使用例

Set は集合のことですが、これはキーと値が同じ連想配列 (Map) として実装されているようです(私見)。このサンプルは、集合に要素を追加していき、ちゃんと要素が含まれているか確認しています。集合なので同じ要素を追加しても２個目は無視されます。

```html
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
 <script>
  const testSet = () => {
    var s = new Set();
    s.add('a');
    s.add('b');
    s.add('a');
    s.add('c');
    $('#set1').html('a: ' + s.has('a').toString());
    $('#set2').html('b: ' + s.has('b').toString());
    $('#set3').html('c: ' + s.has('c').toString());
    $('#set4').html('d: ' + s.has('d').toString());
  }

  $(document).ready(() => {
    testMap();
    testSet();
  });
 </script>
```

スクリプトの実行結果
a: true
b: true
c: true
d: false


# Map / Set のメンバー

Map および Set は Function を継承している。

##Map Properties

- **Map.prototype** 
プロトタイプ
- **Map.prototype.size**
要素 (エントリー) の数
- **Map.prototype[@@toStringTag]**
Map の初期値
- **get Map[@@species]**
コンストラクタ

##Map Methods

- **Map.prototype.clear()**
全要素をクリアする。
- **Map.prototype.delete(key)**
key を持つ要素を削除する。
- **Map.prototype.entries()**
要素(エントリー)のイテレータを返す。
- **Map.prototype.forEach(callback[, thisArg])**
callback を全要素に対して繰り返す。
- **Map.prototype.get(key)**
key で指定した要素の値を返す。
- **Map.prototype.has(key)**
key が存在するか検査する。
- **Map.prototype.keys()**
全キーの配列を返す。
- **Map.prototype.set(key, value)**
キーと値のペアを要素として追加する。
- **Map.prototype.values()**
全値の配列を返す。
- **Map.prototype[[@@iterator]]()**
for of 構文などで使用するイテレータを用意する。

##Set Properties

- **Set.prototype**
プロトタイプ
- **Set.prototype.size**
要素の数
-- *get Set[@@species]**
コンストラクタ

##Set Methods

- **Set.prototype.add(value)**
value を追加する。
- **Set.prototype.clear()**
全要素をクリアする。
- **Set.prototype.delete(value)**
value を持つ要素を削除する。
- **Set.prototype.entries()**
要素(エントリー)のイテレータを返す。
- **Set.prototype.forEach(callback[, thisArg])**
callback を全要素に対して繰り返す。
- **Set.prototype.has(value)**
value の有無を検査する。
- **Set.prototype.values()**
要素(値)のイテレータを返す。
- **Set.prototype[[@@iterator]]()**
for of 構文などで使用するイテレータを用意する。

#逆引きサンプル

##初期化された Map を作成するには

キーと値のペアの配列をパラメータとして Map を構築します。

```JavaScript
var m = new Map([['A',0x41], ['B', 0x42]]);

for (let i of m) {
    console.log("%s => %i", i[0], i[1]);
}
```

**実行例**
A => 65
B => 66

##キー:値のペアを Map に追加するには

set(key, value) メソッドを使って要素を追加します。key がすでに含まれていると上書きになります。

```JavaScript
var m = new Map();
m.set("a", 0);
m.set("b", 1);
m.set("c", 2);

for (let e of m) {
    console.log("%s => %i", e[0], e[1]);
}
```

**実行例**
a => 0
b => 1
c => 2

##初期化された Set を作成するには

配列をパラメータとしてコンストラクタを呼び出します。

```JavaScript
var s = new Set(["a", "b", "c"]);
for (let x of s) {
  console.log(x);
}
```

**実行例**
a
b
c

##値を Set に追加するには

set(value) メソッドを使って要素を追加します。value がすでに含まれていると何もしません。

```JavaScript
var s = new Set();
s.set("A");
s.set("B");

for (let x of s) {
  console.log(x);
}
```

**実行例**
A
B

##キー (Map) や値 (Set) が存在するか調べるには
has() メソッドの値が真かどうかを調べます。

```JavaScript
var m = new Map();
m.set("a", 0);
m.set("b", 1);
m.set("c", 2);

if (m.has("a")) {
  // true
  console.log("a exists.");
}

var s = new Set();
s.set("a");
s.set("b");
if (s.has("c")) {
  // false
  console.log("c exists.");
}
```

**実行例**
a exists.

##すべての要素に対して処理を繰り返すには

for of 構文の他、Map, Set とも forEach メソッドも利用できます。

```JavaScript
var m = new Map();
m.set("a", 0);
m.set("b", 1);
m.set("c", 2);

m.forEach((v, k) => console.log("%s => %i", k, v));

var s = new Set();
s.add("a");
s.add("b");

s.forEach((v) => console.log(v));
```

**実行例**
a => 0
b => 1
c => 2
a
b

##要素を削除するには

Map の場合はキーをパラメータとして delete(key) メソッドを呼び出します。Set の場合は値をパラメータとして delete(value) メソッドを呼び出します。

```JavaScript
var m = new Map([["A", 0], ["B", 1], ["C", 2]]);
m.delete("B");
m.forEach((v, k) => console.log("%s => %i", k, v));

var s = new Set(["a", "b", "c"]);
s.delete("b");
s.forEach((v) => console.log(v));
```

**実行例**
A => 0
C => 2
a
c

##すべての要素をクリアするには

Map, Set とも clear() メソッドを使用します。

```JavaScript
console.log(m.size);
m.clear();
console.log(m.size);  // 0 と表示
console.log(s.size);
s.clear();
console.log(s.size);  // 0 と表示
```

#WeakMap / WeakSet とは何か？

※ この内容は初心者向けではありません。初心者は読み飛ばしてください。
※ キーが文字列、シンボル、数などの場合、Map を使うほうがよいです。
※ 値が文字列、シンボル、数などの場合、Set を使うほうがよいです。

##WeakMap

Map とキーの等価性評価の方法が異なっています。Map ではキーが === 演算子で等価でないと同じとはみなされません。これはキーとしてオブジェクトを使う場合、オブジェクトがガーベージコレクションにより処分されないことが必要になり、使用済みになってもメモリにずっと存在し続けることになり効率が悪くなります。

WeakMap はそのような不都合を解消するため、「弱い参照」を利用して、使用済みのオブジェクトを処分できるようにするものです。(自己解釈ですが === でなく == で同じならいいという感じ)

##WeakSet

WeakSet も WeakMap と同様にキーの代わりに値が評価されます。(※ Set は 特殊な Map として実装されています)


終わり