Map<K, V>
MapはJavaScriptの組み込みAPIのひとつで、キーと値のペアを取り扱うためのオブジェクトです。Mapにはひとつのキーについてはひとつの値のみを格納できます。
Mapオブジェクトの作り方
Mapオブジェクトを作るにはMapクラスをnewします。たとえば、キーがstring、値がnumberのMap<string, number>は次のように作ります。
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map .get ("a"));
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map .get ("a"));
コンストラクタにキーと値の[タプル型][K, V]の配列[K, V][]を渡すとMap<K, V>オブジェクトが作られます。
📄️ タプル
TypeScriptの関数は1値のみ返却可能です。戻り値に複数の値を返したい時に、配列に返したいすべての値を入れて返すことがあります。なお次の関数の戻り値は定数になっていますが、実際は演算した結果だと解釈してください。
tsconstmap = newMap <string, number>([["a", 1],["b", 2],["c", 3],]);console .log (map );
tsconstmap = newMap <string, number>([["a", 1],["b", 2],["c", 3],]);console .log (map );
Mapの型変数を省略した場合、TypeScriptはコンストラクタ引数からMap<K, V>の型を推論します。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);map ;
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);map ;
コンストラクタ引数を省略した場合、空のMapが作られます。
tsconstmap = newMap <string, number>();console .log (map );
tsconstmap = newMap <string, number>();console .log (map );
型引数とコンストラクタ引数の両方を省略した場合、Map<any, any>型になります。
tsconstmap = newMap ();
tsconstmap = newMap ();
Mapの型注釈
TypeScriptでMapの型注釈をする場合は、Map<string, number>のようにMap要素の型を型変数に指定します。
tsfunctiondoSomething (map :Map <string, number>) {}
tsfunctiondoSomething (map :Map <string, number>) {}
Mapのキーは厳密等価で判定される
Mapのキーが同じかどうかは厳密等価(===)で判定します。等価(==)ではありません。
たとえば、nullとundefinedは等価ですが、厳密等価では等しくありません。
tsconsole .log (null ==undefined );console .log (null ===undefined );
tsconsole .log (null ==undefined );console .log (null ===undefined );
そのため、Mapはnullとundefinedを異なるキーとみなします。
tsconstmap = newMap <any, any>([[null, 1]]);console .log (map .has (null));console .log (map .has (undefined ));
tsconstmap = newMap <any, any>([[null, 1]]);console .log (map .has (null));console .log (map .has (undefined ));
NaN同士は厳密等価ではありませんが、例外的に同じキーとみなされます。
js// JavaScriptconsole .log (NaN ===NaN );
js// JavaScriptconsole .log (NaN ===NaN );
tsconstmap = newMap <number, number>();map .set (NaN , 1);map .set (NaN , 2);console .log (map );
tsconstmap = newMap <number, number>();map .set (NaN , 1);map .set (NaN , 2);console .log (map );
オブジェクトは等価でも厳密等価でもないため、別のキーとみなされます。
js// JavaScriptconsole .log ({} == {});console .log ({} === {});
js// JavaScriptconsole .log ({} == {});console .log ({} === {});
tsconstmap = newMap <object, number>();map .set ({}, 1);map .set ({}, 2);console .log (map );
tsconstmap = newMap <object, number>();map .set ({}, 1);map .set ({}, 2);console .log (map );
Mapの操作
要素をセットする - Map.prototype.set()
Mapにキーと値のペアを追加するにはsetメソッドを使います。
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map );
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map );
すでにキーがある場合は、値を上書きします。
tsconstmap = newMap ([["a", 1]]);map .set ("a", 5);console .log (map );
tsconstmap = newMap ([["a", 1]]);map .set ("a", 5);console .log (map );
値を取得する - Map.prototype.get()
Mapからキーをもとに要素を取得するにはgetメソッドを使います。
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("a"));
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("a"));
getメソッドは、キーが存在しない場合、undefinedを返します。
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b"));
Null合体演算子と組み合わせることによってgetメソッドで値を取得できなかったときにデフォルトの値を代入することができます。
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b") ?? 2);
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b") ?? 2);
特定の要素を削除する - Map.prototype.delete()
Mapからキーを指定して要素を削除するにはdeleteメソッドを使います。
tsconstmap = newMap ([["a", 1],["b", 2],]);map .delete ("a");console .log (map );
tsconstmap = newMap ([["a", 1],["b", 2],]);map .delete ("a");console .log (map );
deleteの戻り値は、キーが存在した場合true、そうでない場合falseになります。
tsconstmap = newMap ([["a", 1]]);console .log (map .delete ("a"));console .log (map .delete ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .delete ("a"));console .log (map .delete ("b"));
キーの有無を確認する - Map.prototype.has()
Mapにキーが存在するかどうかを調べるにはhasメソッドを使います。
tsconstmap = newMap ([["a", 1]]);console .log (map .has ("a"));console .log (map .has ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .has ("a"));console .log (map .has ("b"));
要素が存在するかをhasチェックしてから、getで要素を取得するコードはTypeScriptではうまく書けません。
tsconstmap = newMap ([["a", 1]]);if (map .has ("a")) {// TypeScriptは"a"があることを認識しないconstn =map .get ("a");'n' is possibly 'undefined'.18048'n' is possibly 'undefined'.* 2; n }
tsconstmap = newMap ([["a", 1]]);if (map .has ("a")) {// TypeScriptは"a"があることを認識しないconstn =map .get ("a");'n' is possibly 'undefined'.18048'n' is possibly 'undefined'.* 2; n }
この場合、getで値を取得して、その値がundefinedでないことをチェックするとうまくいきます。
tsconstmap = newMap ([["a", 1]]);constn =map .get ("a");if (typeofn === "number") {n * 2;}
tsconstmap = newMap ([["a", 1]]);constn =map .get ("a");if (typeofn === "number") {n * 2;}
要素の個数を取得する - Map.prototype.size()
Mapに登録されている要素数を調べるにはsizeフィールドの値を見ます。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );
全要素を削除する - Map.prototype.clear()
Mapに登録されている要素をすべて削除するにはclearメソッドを使います。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );map .clear ();console .log (map .size );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );map .clear ();console .log (map .size );
キーを列挙する - Map.prototype.keys()
keysメソッドはキーの反復可能オブジェクトを返します。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeys = [...map .keys ()];console .log (keys );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeys = [...map .keys ()];console .log (keys );
値を列挙する - Map.prototype.values()
valuesメソッドは値の反復可能オブジェクトを返します。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constvalues = [...map .values ()];console .log (values );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constvalues = [...map .values ()];console .log (values );
キーと値のペアを列挙する - Map.prototype.entries()
entriesメソッドはキーと値の反復可能オブジェクトを返します。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map .entries ()];console .log (keyValues );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map .entries ()];console .log (keyValues );
キーと値のペアを反復する
Mapはfor...ofで反復できます。反復の順序は登録された順です。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);for (const [key ,value ] ofmap ) {console .log (key ,value );// "a", 1// "b", 2// "c", 3 の順で出力される}
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);for (const [key ,value ] ofmap ) {console .log (key ,value );// "a", 1// "b", 2// "c", 3 の順で出力される}
複製する
Mapオブジェクトを複製(シャローコピー)するには、MapオブジェクトをMapコンストラクタに渡します。
tsconstmap1 = newMap ([["a", 1]]);constmap2 = newMap (map1 );console .log (map2 );
tsconstmap1 = newMap ([["a", 1]]);constmap2 = newMap (map1 );console .log (map2 );
Mapは直接JSONにできない
MapオブジェクトはJSON.stringifyにかけても、登録されている要素はJSONになりません。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (JSON .stringify (map ));
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (JSON .stringify (map ));
MapをJSON化する場合は、一度オブジェクトにする必要があります。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (JSON .stringify (obj ));
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (JSON .stringify (obj ));
他の型との相互運用
Mapを配列にする
Map<K, V>にスプレッド構文を使うと、タプル型配列[K, V][]が得られます。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map ];console .log (keyValues );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map ];console .log (keyValues );
オブジェクトをMapにする
オブジェクトをMapに変換するには、Object.entriesの戻り値をMapコンストラクタに渡します。
tsconstobj = {a : 1,b : 2,c : 3 };constmap = newMap (Object .entries (obj ));console .log (map );
tsconstobj = {a : 1,b : 2,c : 3 };constmap = newMap (Object .entries (obj ));console .log (map );
Mapをオブジェクトにする
Mapをオブジェクトにするには、Object.fromEntriesにMapオブジェクトを渡します。
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (obj );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (obj );
Mapとオブジェクトの違い
キーと値のペアが表現ができるという点で、Mapとオブジェクトは似ていますが、次の違いがあります。
| 違い | Map | オブジェクト |
|---|---|---|
| プロトタイプキーの上書き | 起きない | 起こりうる |
| キーに使える型 | 任意の型 | stringかsymbol |
| 反復の順序 | 挿入順 | 複雑なロジック |
| JSON化 | 直接できない | 直接できる |
プロトタイプキーの上書き
オブジェクトはプロトタイプのキーを上書きする可能性があります。
jsconstobj = {};console .log (obj .toString );obj .toString = 1;console .log (obj .toString );
jsconstobj = {};console .log (obj .toString );obj .toString = 1;console .log (obj .toString );
Mapは要素をセットしてもプロトタイプのキーを上書きする心配がありません。要素とプロトタイプは別の領域にあるためです。
tsconstmap = newMap <string, any>();console .log (map .toString );map .set ("toString", 1);console .log (map .toString );
tsconstmap = newMap <string, any>();console .log (map .toString );map .set ("toString", 1);console .log (map .toString );
キーに使える型
オブジェクトのキーに使える型はstring型かsymbol型のどちらです。Mapは任意の型をキーにできます。
反復の順序
オブジェクトのプロパティの反復順序は、書いた順や追加した順ではなく、複雑なロジックになっています。
📄️ オブジェクトをループする方法
JavaScript・TypeScriptでオブジェクトのプロパティをループする方法を説明します。
Mapの要素の反復順序は要素を追加した順であることが保証されています。
JSON化
オブジェクトはそのままJSON.stringifyでJSON化できます。MapはJSON.stringifyしても要素はJSONになりません。一度Mapをオブジェクトに変換する必要があります。
Mapとオブジェクトの書き方比較
Mapとオブジェクトは似た操作ができます。次がその対応表です。
Map | オブジェクト | |
|---|---|---|
| 型注釈の書き方 | Map<K, V> | Record<K, V> |
| 初期化 | new Map([["a", 1]]) | { a: 1 } |
| 要素のセット | map.set(key, value) | obj[key] = value |
| 値の取得 | map.get(key) | obj[key] |
| 要素の削除 | map.delete(key) | delete obj.key |
| キーの有無確認 | map.has(key) | key in obj |
| 要素数の取得 | map.size | Object.keys(obj).length |
| 全要素削除 | map.clear() | - |
| キーの列挙 | map.keys() | Object.keys(obj) |
| 値の列挙 | map.values() | Object.values(obj) |
| 要素の列挙 | map.entries() | Object.entries(obj) |
| 複製 | new Map(map) | { ...obj } |
📄️ Record<Keys, Type>
キー・バリューからオブジェクト型を作る
学びをシェアする
🗺Mapはキーと値のペアを扱うJSビルトインのAPI
📝TypeScriptではMap<string, number>のように型注釈する
🔬キーは厳密等価で判定される
🔪Mapは直接JSON化できない
⚖️Mapとオブジェクトの違い
→ Mapはキーに任意の型が使える
→ Mapはキーの順序が挿入順保証
『サバイバルTypeScript』より