`Map` オブジェクト

`Map` オブジェクト

この記事ではMap オブジェクトについて説明します。

基本操作から実務で役立つ応用例まで、ステップバイステップで解説します。

YouTube Video

Map オブジェクト

Map はキーと値の組を保持するコレクションです。オブジェクトと似ていますが、オブジェクト、関数、プリミティブなどの任意の型もキーにできる点や、挿入順序を保持する点などで異なります。

Mapの基本

まずは Map の生成と基本的な操作方法を見てみます。

以下のコードは、空のマップを作り、キーを追加して値を取得する最小例です。

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • このコードでは set で要素を追加、get で値を取得、size で要素数を確認できます。
  • Map は挿入順序を保持するため、順序に依存する処理にも向いています。

set, get, has, delete の挙動

典型的な読み書きと存在チェック、削除の例を示します。

次のコードでは各メソッドの戻り値や効果を確認できます。

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • get はキーがなければ undefined を返します。has は存在を確認し、delete はキーを削除します。
  • なお set はマップ自体を返すため、メソッドチェーンが可能です。

任意の型をキーにできる(オブジェクトキーの利用)

Mapの最大の利点の一つは、オブジェクトをそのままキーにできる点です。

次の例はオブジェクトをキーにしてMapで値を関連付ける例です。

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • オブジェクトをキーに使う場合、同一参照であることが重要です。同じ内容でも、参照が異なるオブジェクトは別物として扱われ、同じキーにはなりません。

イテレーション(反復処理)

Map は挿入順序を保持するので、反復処理がよく使われます。

以下では for...offorEach、そして keys()values()entries() の使い方を示します。

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries()[key, value] の配列を返し、スプレッド構文で配列化できます。forEach のコールバックは value, key の順で渡される点に注意してください。

MapObjectの変換

既存のオブジェクトをMapに変換したり、Mapを通常のオブジェクトや配列に変換することもできます。

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • Object.entriesObject.fromEntries を活用すると変換が簡単にできます。ただし、オブジェクトのキーは文字列またはシンボルに限定されるため、オブジェクトに戻すと非文字列キーは失われます。

実務で役立つパターン:頻度カウント(カウントマップ)

配列の中で同じ要素の出現回数を数えるときは、Map を使うと処理がシンプルになります。

以下のコードは、マップを使って、文字列配列の頻度を数えてソートする例です。

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • Map を使うことで、存在チェックと更新が簡単に書けます。オブジェクトでも可能ですが、任意のキーを扱う点で Map の方が直感的な場合があります。

MapJSON.stringifyの注意点(シリアライズ)

JSON.stringifyMap を直接シリアライズできません。Mapを保存する場合は変換が必要です。

次の例はマップを配列にしてから JSON にする方法と、復元の方法です。

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • 保存や送受信が必要なマップは、配列に変換してからシリアライズします。復元もJSON.parseで配列に変換してからマップにします。

WeakMapの紹介と使い分け

WeakMap はキーが弱参照(ガベージコレクションの対象)である点が異なります。

オブジェクトをキーにしたキャッシュやメタ情報を保持し、キーオブジェクトの寿命に合わせて自動的に解放したいときに有用です。

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap は列挙できない、サイズ確認ができないなどの制約がありますが、メモリリーク防止に有用です。

まとめ

Map は、オブジェクトとは違ってあらゆる型をキーにでき、挿入順序もそのまま保持できる使い勝手のよいコレクションです。基本操作から応用まで理解しておくことで、データ管理をより柔軟かつ直感的に行えるようになります。用途に合わせて Object と使い分けることで、コードの明瞭さや可読性も大きく向上します。

YouTubeチャンネルでは、Visual Studio Codeを用いて上記の記事を見ながら確認できます。 ぜひYouTubeチャンネルもご覧ください。

YouTube Video