Map はどんな値でも鍵にでき、Set は重複を持ちません。オブジェクトや配列と何が違うのか、同じ値とみなす基準、JSON にできない理由、集合の演算までを実行して確かめます。
Map は鍵と値の組を持つ入れ物です。オブジェクトと似ていますが、鍵に何でも使えます。
example.js
const scores = new Map();
scores.set('国語', 80).set('数学', 95);
console.log(scores);
console.log(scores.get('国語'));
console.log(scores.get('無い教科'));
console.log(scores.has('数学'), scores.size);
出力書き換えて実行できます
Map(2) { '国語' => 80, '数学' => 95 }
80
undefined
true 2
example.js
console.log(new Set([1, 2, 2, 3]));
console.log(new Set('あいあ'));
出力書き換えて実行できます
Set(3) { 1, 2, 3 }
Set(2) { 'あ', 'い' }
Set は重複しない値の集まりです。
example.js
const obj = {};
obj[1] = '数';
obj['1'] = '文字';
console.log(obj);
console.log(Object.keys(obj));
出力書き換えて実行できます
{ '1': '文字' }
[ '1' ]
example.js
const map = new Map([[1, '数'], ['1', '文字']]);
console.log(map.size);
console.log(map.get(1), map.get('1'));
出力書き換えて実行できます
2
数 文字
オブジェクトとの違い
オブジェクトの鍵は、文字列か Symbol にしかなりません。
example.js
const obj = {};
console.log(obj['toString'] !== undefined);
console.log(new Map().has('toString'));
出力書き換えて実行できます
true
false
example.js
console.log(new Set([NaN, NaN]).size);
console.log(new Set([0, -0]).size);
console.log(new Set([{}, {}]).size);
出力書き換えて実行できます
1
1
2
1と '1' が同じ鍵になり、後から書いたほうで上書きされました。
Map なら別々に持てます。
example.js
const key = { id: 1 };
const map = new Map([[key, '見つかる']]);
console.log(map.get(key));
console.log(map.get({ id: 1 }));
出力書き換えて実行できます
見つかる
undefined
example.js
const map = new Map([['b', 2], ['a', 1]]);
console.log([...map.keys()]);
console.log([...map.values()]);
console.log([...map]);
出力書き換えて実行できます
[ 'b', 'a' ]
[ 2, 1 ]
[ [ 'b', 2 ], [ 'a', 1 ] ]
|
オブジェクト |
Map |
| 鍵にできるもの |
文字列 / Symbol |
何でも |
| 件数 |
Object.keys().length |
size |
| 順番 |
整数キーが先に来る |
入れた順 |
| 初めから入っているもの |
toString など |
空 |
| JSON |
そのまま書ける |
書けない |
継承したものが混ざらない
オブジェクトには、書いた覚えのない鍵が最初から存在します。
example.js
for (const [subject, score] of new Map([['国語', 80], ['数学', 95]])) {
console.log(subject, score);
}
出力書き換えて実行できます
国語 80
数学 95
example.js
new Map([['a', 1]]).forEach((value, key) => console.log(key, value));
出力書き換えて実行できます
a 1
利用者の入力を鍵にする(辞書として使う)なら、Map のほうが安全です。
オブジェクトだと、toString や constructor という入力が来たときに壊れます。
何を「同じ」とみなすか
Map の鍵と Set の値は、SameValueZeroという基準で比べます。
example.js
const map = new Map(Object.entries({ a: 1, b: 2 }));
console.log(map);
console.log(Object.fromEntries(map));
出力書き換えて実行できます
Map(2) { 'a' => 1, 'b' => 2 }
{ a: 1, b: 2 }
example.js
const words = ['あ', 'い', 'あ', 'う', 'あ'];
const count = new Map();
for (const word of words) {
count.set(word, (count.get(word) ?? 0) + 1);
}
console.log(count);
出力書き換えて実行できます
Map(3) { 'あ' => 3, 'い' => 1, 'う' => 1 }
NaN は自分自身と同じとみなされます(=== では false なのに)
0 と -0 は同じ
- 見た目が同じでも、別のオブジェクトは別
オブジェクトは中身ではなく、そのもので比べられます。
example.js
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
console.log(a.union(b));
console.log(a.intersection(b));
console.log(a.difference(b));
console.log(a.isSubsetOf(new Set([1, 2, 3, 4])));
出力書き換えて実行できます
Set(4) { 1, 2, 3, 4 }
Set(2) { 2, 3 }
Set(1) { 1 }
true
javascript
console.log(JSON.stringify(new Map([['a', 1]])));
{}
同じ形を作り直しても取り出せません。 これが Map のいちばんの落とし穴です。
→ 配列から重複を取り除く
順番と取り出し方
どちらも入れた順を保ちます。
example.js
const map = new Map([['a', 1]]);
console.log(JSON.stringify([...map]));
console.log(JSON.stringify(Object.fromEntries(map)));
出力書き換えて実行できます
[["a",1]]
{"a":1}
javascript
console.log(new Set([1, 2, 3]).length);
undefined
for...of で回すと、鍵と値の組が来ます。
forEach() は値が先、鍵が後です。配列の forEach() と同じ並びに揃えてあります。
引数の順を取り違えやすいので、for...of のほうを勧めます。
オブジェクトとの行き来
Object.entries() と Object.fromEntries() で往復できます。
→ Object.keys() / values() / entries()
数えるのに向いている
Map のいちばん多い使い道です。
get() が undefined を返すので、?? で0を補います。
集合の演算
Set 同士の計算ができます(ES2025)。
比較的新しい機能です。Node 22 / Chrome 122 / Safari 17 / Firefox 127 より前では使えません。
よくある間違い
そのまま JSON.stringify() に渡す
中身が消えます。 例外も出ません。
Map も Set も、JSON にそのままは書けません。配列かオブジェクトに直してください。
鍵が文字列だけなら Object.fromEntries()、
そうでないなら組の配列にして保存します。
length を見る
Map と Set は size です。配列と混ぜないでください。
何でもかんでも Map にする
鍵が文字列で決まっていて、件数も少ないなら、オブジェクトのほうが読みやすくなります。
Map を選ぶのは、次のどれかに当てはまるときです。
- 鍵が文字列以外(オブジェクト・数値を区別したい)
- 鍵が実行時に決まる(利用者の入力・外部データ)
- 出し入れが頻繁で、件数を数えたい
まとめ
Map の鍵は何でもよい。オブジェクトは文字列に変えられてしまう
- 比較はSameValueZero。
NaN は同じ、別オブジェクトは別
- どちらも入れた順を保つ。件数は
size
forEach() は値が先。for...of のほうが間違えにくい
- JSON にそのままは書けない。 黙って
{} になる
- 利用者の入力を鍵にするなら
Map。継承した鍵が混ざらない