JavaScript | ES6+ 文法:新データ構造 – Map

JavaScript JavaScript
スポンサーリンク

Map とは何か(まずイメージから)

Map は、ES6 で追加された
「キーと値のペアを柔軟に保存できる専用のコレクション」 です。

似たものに「オブジェクト {}」がありますが、Map には大きな違いがあります。

  • オブジェクト
    • キーは基本「文字列 or シンボル」
    • プロトタイプの影響を受ける
  • Map
    • キーに「どんな値でも」使える(オブジェクト、配列、関数など)
    • 挿入した順番を保つ
    • サイズを簡単に取得できる

まずはざっくり使い方を見てみます。

const m = new Map();

// 追加
m.set("name", "Alice");
m.set("age", 20);

// 取得
console.log(m.get("name")); // Alice

// 存在チェック
console.log(m.has("age"));  // true

// 削除
m.delete("age");

// 要素数
console.log(m.size);        // 1
JavaScript

ここが重要です。
Map は「キーと値」を扱う点ではオブジェクトと似ているが、「キーとして使える範囲」と「動作の素直さ」がかなり違う専用コレクション です。
特に「オブジェクトをキーにしたい」時は Map 一択です。

Map の基本操作(set / get / has / delete)

Map の作り方と set での追加

const userMap = new Map();

userMap.set("id", 1);
userMap.set("name", "Alice");

console.log(userMap);
// Map(2) { 'id' => 1, 'name' => 'Alice' }
JavaScript

set(key, value) でキーと値のペアを追加します。
同じキーに再度 set すると、上書きになります。

userMap.set("name", "Bob");
console.log(userMap.get("name")); // Bob
JavaScript

get で取得、has で存在チェック

console.log(userMap.get("id"));  // 1
console.log(userMap.get("age")); // 存在しない → undefined

console.log(userMap.has("id"));  // true
console.log(userMap.has("age")); // false
JavaScript

get は存在しないキーに対して undefined を返します。
「キーがあるかどうか」を知りたいときは has を使うと安全です。

delete と clear で削除

userMap.delete("id"); // 指定キーを削除
console.log(userMap.has("id")); // false

userMap.clear();      // 全削除
console.log(userMap.size); // 0
JavaScript

size プロパティで要素数が一発で分かる

const m = new Map();
m.set("a", 1);
m.set("b", 2);

console.log(m.size); // 2
JavaScript

オブジェクト {} の場合、プロパティ数を数えるには Object.keys(obj).length のような書き方が必要でしたが、
Map は size で即座に分かります。

ここが重要です。
「キーの追加、削除、個数確認」を頻繁にやるなら、オブジェクトより Map の方が素直で扱いやすい です。

オブジェクトとの大きな違い(ここを深掘り)

違い1:キーに「何でも」使える(オブジェクトも配列も)

オブジェクト {} ではキーは文字列(とシンボル)だけですが、
Map では オブジェクト自体をキーにできます

const m = new Map();

const objKey = { id: 1 };
const arrKey = [1, 2, 3];

m.set(objKey, "オブジェクトに紐づく値");
m.set(arrKey, "配列に紐づく値");

console.log(m.get(objKey)); // オブジェクトに紐づく値
console.log(m.get(arrKey)); // 配列に紐づく値
JavaScript

ID → ユーザー情報、という形ではなく、
ユーザーオブジェクト → 関連データ のように「そのもの」をキーにしたいときに非常に便利です。

ここが重要です。
「オブジェクトをキーにしたい」= 普通のオブジェクトではほぼ無理(プロパティ名に文字列化されてしまう)
→ Map ならそのままキーとして扱える。

違い2:挿入順序を保持してくれる

Map は、
「キーを追加した順番」のままループできます

const m = new Map();
m.set("first", 1);
m.set("second", 2);
m.set("third", 3);

for (const [key, value] of m) {
  console.log(key, value);
}
// first 1
// second 2
// third 3
JavaScript

オブジェクトも最近の仕様ではある程度順序が保たれますが、
Map は「順序付きのキー集合」として公式に設計されています。

違い3:プロトタイプや特別なキーに悩まされない

オブジェクト {} には、toString などのプロトタイプから継承したプロパティがあります。

const obj = {};
console.log("toString" in obj); // true
JavaScript

Map は純粋な「キー → 値」のコレクションなので、
toString のような紛らわしいキーは最初から存在しません。

const m = new Map();
console.log(m.has("toString")); // false
JavaScript

オブジェクトでキーを扱うときにややこしい「特別扱いのプロパティ」に悩まされないのは、Map の大きな利点です。

Map のイテレーション(for…of / keys / values / entries)

Map 自体を for…of すると [key, value] のペアになる

const m = new Map();
m.set("name", "Alice");
m.set("age", 20);

for (const [key, value] of m) {
  console.log(key, value);
}
// name Alice
// age 20
JavaScript

分割代入 [key, value] がとても相性よく使えます。

keys / values / entries でそれぞれ取り出す

const m = new Map();
m.set("a", 1);
m.set("b", 2);

for (const key of m.keys()) {
  console.log("key:", key);
}

for (const value of m.values()) {
  console.log("value:", value);
}

for (const [key, value] of m.entries()) {
  console.log(key, value);
}
JavaScript

mm.entries() は同じ動き([key, value] ペア)をします。

forEach も使える

m.forEach((value, key) => {
  console.log(key, value);
});
JavaScript

コールバックの引数の順番が (value, key) であることに注意してください。
(オブジェクトの Array.prototype.forEach と同じ順序です)

よくある用途と具体例

例1:オブジェクトをキーにして追加情報を持たせる

const user1 = { id: 1, name: "Alice" };
const user2 = { id: 2, name: "Bob" };

const extraInfo = new Map();

extraInfo.set(user1, { lastLogin: "2025-01-01" });
extraInfo.set(user2, { lastLogin: "2025-01-05" });

console.log(extraInfo.get(user1)); // { lastLogin: "2025-01-01" }
JavaScript

user1user2 に直接プロパティを追加しても良いですが、
「元のオブジェクトは触りたくない」「外から渡されたオブジェクトに対して付加情報を持ちたい」
といった場面で Map が役立ちます。

例2:頻度カウンター(出現回数を数える)

文字列中の各文字が何回出てくるかを数える例です。

function countChars(str) {
  const map = new Map();

  for (const ch of str) {
    const count = map.get(ch) || 0;
    map.set(ch, count + 1);
  }

  return map;
}

const result = countChars("hello");

for (const [ch, count] of result) {
  console.log(ch, count);
}
// h 1
// e 1
// l 2
// o 1
JavaScript

ここが重要です。
「キーの存在チェック → 無ければ 0 から → 1 足す」
という処理をするとき、has / get / set が非常に素直に使えます。
オブジェクトでも書けますが、Map の方が「辞書」操作っぽくて読みやすいです。

例3:順番が重要な設定の管理

例えば、ミドルウェアのように「追加順に処理したい」場合。

const middlewares = new Map();

middlewares.set("auth", (req) => { console.log("認証処理"); });
middlewares.set("logging", (req) => { console.log("ログ処理"); });

function handleRequest(req) {
  for (const [name, fn] of middlewares) {
    console.log(`--- ${name} ---`);
    fn(req);
  }
}

handleRequest({});
// --- auth ---
// 認証処理
// --- logging ---
// ログ処理
JavaScript

追加順序が保証されるので、「登録した順に実行する」というロジックが自然に書けます。

オブジェクトと Map をどう使い分けるか

オブジェクト向きの場面

オブジェクト {} は、主に「固定された構造のデータ」に向いています。

  • ユーザー1人の情報(id, name, age など)
  • API から返ってくる JSON
  • クラスのインスタンスのプロパティ
const user = {
  id: 1,
  name: "Alice",
  age: 20,
};
JavaScript

「キーの種類があらかじめ決まっていて、増えたり減ったりしない」ようなデータです。

Map 向きの場面

Map は、主に「可変なキー集合」「辞書・マップ」として使うのに向いています。

  • 任意のキー(特にオブジェクト)を使いたい
  • 要素を頻繁に追加・削除する
  • キーの数を簡単に知りたい
  • 挿入順序に意味がある
const cache = new Map();
cache.set("user:1", { id: 1, name: "Alice" });
cache.set("user:2", { id: 2, name: "Bob" });
JavaScript

ここが重要です。
「構造化された1件のデータ」→ オブジェクト
「キーと値の集まり(辞書・マップ)」→ Map

という大まかな切り分けを頭に置いておくと、かなり選びやすくなります。

Map の初期化と便利テク

二次元配列から初期化

Map は、[キー, 値] のペアの配列から作ることができます。

const m = new Map([
  ["apple", 100],
  ["banana", 150],
  ["orange", 120],
]);

console.log(m.get("banana")); // 150
JavaScript

CSV や JSON など外部データから Map を作るときに便利です。

Map から配列へ変換

逆に、Map を配列に変換するのも簡単です。

const arr = [...m];  // [[key, value], [key, value], ...]
console.log(arr);

const keys = [...m.keys()];
const values = [...m.values()];
JavaScript

Array.from(m) でも同じように配列化できます。


まとめ

Map の核心は、
「キーに何でも使えて、順序付きで、操作が素直な“辞書”のようなコレクション」 であることです。

押さえておきたいポイントを整理すると:

  • set, get, has, delete, clear, size が基本操作
  • キーにオブジェクト・配列・関数など何でも使える
  • 挿入順序を保ち、for...ofkeys, values, entries, forEach で走査できる
  • オブジェクト {} は「1件の構造化データ」に、Map は「キーと値の可変な集まり」に向いている
  • 頻度カウンタ、オブジェクトをキーにした追加情報、順序付き設定などで特に威力を発揮する

まずは、オブジェクトで書いていた「辞書」のような処理を、
一度 Map に書き換えてみてください。

「キー存在チェック → 追加・更新 → ループ」の流れが、
Map だとどれだけ素直に書けるかが、実感として分かってくるはずです。

タイトルとURLをコピーしました