---
type: reference
language: javascript
slug: collection/map-set
title: "Map と Set"
title_tag: "JavaScript Map と Set の使い方 — オブジェクトとの違い"
summary: >
  Map はどんな値でも鍵にでき、Set は重複を持ちません。オブジェクトや配列と何が違うのか、
  同じ値とみなす基準、JSON にできない理由、集合の演算までを実行して確かめます。
description: >
  Map は数値の1と文字列の1を別の鍵として持てます。オブジェクトが文字列に変換される違い、NaN を同じとみなす基準、JSON にすると中身が消える点まで実行して確かめます。
status: published
difficulty: 2
minutes: 10

versions:
  verified: "Node 22.22.3"
  since: "ES2015"
  deprecated: null
  removed: null

sources:
  - title: "Map Objects — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-map-objects"
  - title: "Set Objects — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-set-objects"
  - title: "Set — MDN"
    url: "https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Set"

terms: [Map, Set, SameValueZero, 挿入順]

links:
  related:
    - javascript/how-to/array/remove-duplicates
    - javascript/reference/object/keys
    - javascript/how-to/array/group
    - javascript/how-to/array/compare

content_updated_at: 2026-09-08
published_at: 2026-09-08
---

`Map` は[key:鍵と値の組]を持つ入れ物です。オブジェクトと似ていますが、**鍵に何でも使えます。**

```js run
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);
```
```output
Map(2) { '国語' => 80, '数学' => 95 }
80
undefined
true 2
```

`Set` は[key:重複しない値]の集まりです。

```js run
console.log(new Set([1, 2, 2, 3]));
console.log(new Set('あいあ'));
```
```output
Set(3) { 1, 2, 3 }
Set(2) { 'あ', 'い' }
```

## オブジェクトとの違い

**オブジェクトの鍵は、文字列か Symbol にしかなりません。**

```js run
const obj = {};

obj[1] = '数';
obj['1'] = '文字';

console.log(obj);
console.log(Object.keys(obj));
```
```output
{ '1': '文字' }
[ '1' ]
```

[num:1]と `'1'` が[bad:同じ鍵になり]、後から書いたほうで上書きされました。

`Map` なら別々に持てます。

```js run
const map = new Map([[1, '数'], ['1', '文字']]);

console.log(map.size);
console.log(map.get(1), map.get('1'));
```
```output
2
数 文字
```

| | オブジェクト | `Map` |
|---|---|---|
| 鍵にできるもの | 文字列 / Symbol | [em:何でも] |
| 件数 | `Object.keys().length` | `size` |
| 順番 | 整数キーが先に来る | [key:入れた順] |
| 初めから入っているもの | `toString` など | 空 |
| JSON | そのまま書ける | [bad:書けない] |

### 継承したものが混ざらない

オブジェクトには、書いた覚えのない鍵が最初から存在します。

```js run
const obj = {};

console.log(obj['toString'] !== undefined);
console.log(new Map().has('toString'));
```
```output
true
false
```

利用者の入力を鍵にする（辞書として使う）なら、**`Map` のほうが安全です。**
オブジェクトだと、`toString` や `constructor` という入力が来たときに壊れます。

## 何を「同じ」とみなすか

`Map` の鍵と `Set` の値は、[type:SameValueZero]という基準で比べます。

```js run
console.log(new Set([NaN, NaN]).size);
console.log(new Set([0, -0]).size);
console.log(new Set([{}, {}]).size);
```
```output
1
1
2
```

- **`NaN` は自分自身と同じ**とみなされます（`===` では `false` なのに）
- `0` と `-0` は同じ
- **見た目が同じでも、別のオブジェクトは別**

オブジェクトは[key:中身ではなく、そのもの]で比べられます。

```js run
const key = { id: 1 };
const map = new Map([[key, '見つかる']]);

console.log(map.get(key));
console.log(map.get({ id: 1 }));
```
```output
見つかる
undefined
```

**同じ形を作り直しても取り出せません。** これが `Map` のいちばんの落とし穴です。

→ [配列から重複を取り除く](/ja/javascript/how-to/array/remove-duplicates/)

## 順番と取り出し方

どちらも[key:入れた順]を保ちます。

```js run
const map = new Map([['b', 2], ['a', 1]]);

console.log([...map.keys()]);
console.log([...map.values()]);
console.log([...map]);
```
```output
[ 'b', 'a' ]
[ 2, 1 ]
[ [ 'b', 2 ], [ 'a', 1 ] ]
```

`for...of` で回すと、[key:鍵と値の組]が来ます。

```js run
for (const [subject, score] of new Map([['国語', 80], ['数学', 95]])) {
  console.log(subject, score);
}
```
```output
国語 80
数学 95
```

`forEach()` は**値が先、鍵が後**です。配列の `forEach()` と同じ並びに揃えてあります。

```js run
new Map([['a', 1]]).forEach((value, key) => console.log(key, value));
```
```output
a 1
```

[bad:引数の順を取り違えやすい]ので、`for...of` のほうを勧めます。

## オブジェクトとの行き来

```js run
const map = new Map(Object.entries({ a: 1, b: 2 }));

console.log(map);
console.log(Object.fromEntries(map));
```
```output
Map(2) { 'a' => 1, 'b' => 2 }
{ a: 1, b: 2 }
```

`Object.entries()` と `Object.fromEntries()` で往復できます。
→ [Object.keys() / values() / entries()](/ja/javascript/reference/object/keys/)

## 数えるのに向いている

`Map` のいちばん多い使い道です。

```js run
const words = ['あ', 'い', 'あ', 'う', 'あ'];
const count = new Map();

for (const word of words) {
  count.set(word, (count.get(word) ?? 0) + 1);
}

console.log(count);
```
```output
Map(3) { 'あ' => 3, 'い' => 1, 'う' => 1 }
```

`get()` が `undefined` を返すので、`??` で[num:0]を補います。

## 集合の演算

`Set` 同士の計算ができます（ES2025）。

```js run
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])));
```
```output
Set(4) { 1, 2, 3, 4 }
Set(2) { 2, 3 }
Set(1) { 1 }
true
```

[dim:比較的新しい機能です。Node 22 / Chrome 122 / Safari 17 / Firefox 127 より前では使えません。]

## よくある間違い

### そのまま `JSON.stringify()` に渡す

```js bad
console.log(JSON.stringify(new Map([['a', 1]])));
```
```output
{}
```

**中身が消えます。** 例外も出ません。

`Map` も `Set` も、[bad:JSON にそのままは書けません]。配列かオブジェクトに直してください。

```js run
const map = new Map([['a', 1]]);

console.log(JSON.stringify([...map]));
console.log(JSON.stringify(Object.fromEntries(map)));
```
```output
[["a",1]]
{"a":1}
```

鍵が文字列だけなら `Object.fromEntries()`、
そうでないなら[key:組の配列]にして保存します。

### `length` を見る

```js bad
console.log(new Set([1, 2, 3]).length);
```
```output
undefined
```

`Map` と `Set` は `size` です。**配列と混ぜないでください。**

### 何でもかんでも `Map` にする

鍵が文字列で決まっていて、件数も少ないなら、**オブジェクトのほうが読みやすくなります。**
`Map` を選ぶのは、次のどれかに当てはまるときです。

- 鍵が[key:文字列以外]（オブジェクト・数値を区別したい）
- 鍵が[key:実行時に決まる]（利用者の入力・外部データ）
- **出し入れが頻繁**で、件数を数えたい

## まとめ

- `Map` の鍵は[em:何でもよい]。オブジェクトは文字列に変えられてしまう
- 比較は[type:SameValueZero]。`NaN` は同じ、**別オブジェクトは別**
- どちらも[key:入れた順]を保つ。件数は `size`
- `forEach()` は**値が先**。`for...of` のほうが間違えにくい
- **JSON にそのままは書けない。** 黙って `{}` になる
- 利用者の入力を鍵にするなら `Map`。継承した鍵が混ざらない
