[Javascript] Mapクラスを使った連想配列の定義方法

[Javascript] Mapクラスを使った連想配列の定義方法

Javascriptで連想配列を定義

連想配列(あるいは辞書型やハッシュテーブル)は、従来のJavascriptの機能として直接的には提供されていませんでした。代わりに、Javascriptでは連想配列の機能を実現するためにObjectを使っていました。

// 連想配列を定義
var dictionary = { "a": 100, "b": 200, "c": 300 };

// データの追加・更新
dictionary.d = 400;
dictionary["e"] = 500;

// データの削除
delete dictionary.a;
delete dictionary["b"];

// データの参照
var d = dictionary.d;
var e = dictionary["e"];
console.log(d, e) // 400, 500

// データの列挙
var keys = Object.keys(dictionary)
for(var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = dictionary[key];
    console.log("key:", key, "value:", value);
}

上記の通り、一通りデータの操作は可能ですが、若干無理しているところはあります。特にデータの削除は delete を使ってオブジェクトのプロパティを削除しているので、なんだか変な感じです。

ES2015(ES6) では Mapクラスが使えるようになった

ES2015(ES6) では、Map クラスが正式に提供されるようになったため、Javascriptの機能として連想配列が使えるようになっています。

// 連想配列を定義
// let map = new Map(); // <- 空の連想配列の場合
const map = new Map([
    ["a", 100],
    ["b", 200],
    ["c", 300],
]);
// Map(3) {"a" => 100, "b" => 200, "c" => 300}

// データの追加・更新
map.set("d", 400);
map.set("e", 500);

// データの削除
map.delete("a")

// データの参照
const b = map.get("b");

// キーの存在判定
const flag = map.has("e"); // true

// データの列挙
for(let entry of map.entries()) {
    const key = entry[0];
    const value = entry[1];
    console.log("key:", key, "value:", value);
}

// キーだけ列挙
for(let key of map.keys()) console.log(key);
// 値だけ列挙
for(let value of map.values()) console.log(value);

// 全データ削除
map.clear();

上記例では、文字列をキーにしたMapを定義していますが、キーは任意の型を用いることができます。オブジェクトでは文字列もしくはSymbolしか使えませんでしたが、例えばMapでは数字のキーを使うことができ、なおかつ文字と区別されます。

// "1" と 1 は別物のキーと判断される
const map = new Map();
map.set("1", 100);
map.set(1, 200);

全てのキーが === 演算子の動作に従って等しいと見なされます。

Map クラスの関数とプロパティ

Mapクラスに用意されている関数とプロパティをまとめておきます。

Map.prototype.clear()

Map内の全要素を削除します。

Map.prototype.delete()

指定されたキーに一致する要素を削除します。

map.delete(key);

Map.prototype.entries()

挿入順でMapオブジェクトの要素に対する[key, value]ペアを含む新しい Iterator オブジェクトを返します。

for(let entry of map.entries()) {
    const key = entry[0];
    const value = entry[1];
    console.log("key:", key, "value:", value);
}

Map.prototype.forEach()

Mapの要素に対して挿入順に指定された関数を実行します。引数でキー、バリュー、マップ本体を受け取れます。

上の entries() と同じように使えると思いますが、違いがあまりわかりません。

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

Map.prototype.get()

一致するキーの要素を返します。

map.get(key);

Map.prototype.has()

一致するキーが存在するかを返します。

map.has(key);

Map.prototype.keys()

挿入順にMapオブジェクトの要素のキーを含む新しいIteratorオブジェクトを返します。

Map.prototype.values()

Mapオブジェクトに挿入された要素を挿入順に列挙する新しい イテレーター オブジェクトを返します。

Map.prototype.set()

特定のkeyとvalueをもった新しい要素をMapオブジェクトに追加します。

map.set(key, value);

Mapクラスとオブジェクトの違い

上記の通りMapクラスは操作のための関数を用意してくれているのでObjectを使うより柔軟な対応が可能になっています。そのほか以下のような違いがあります。

オブジェクトとマップの比較

Objects と Map は、両者とも値へのキーを設定したり、それらの値を取り出したり、キーを削除したり、また何かがあるキーに格納されているかを判定したりすることができるという点で似ています。このため、歴史的に Object は Map として使われてきました。しかし、Map の使用を望ましくする Object と Map 間の重要な違いが存在します。


引用元
  • Object のキーは Strings と Symbols ですが、Map では任意の値がキーとなり得ます。
  • Map の大きさは size プロパティで簡単に得ることができます。一方、Object の大きさは手動で保つ必要があります。
  • Map は iterable で ダイレクトに反復処理できる一方、オブジェクトを反復処理するには、何らかの方法でキーを取得し、それらのキーを元に反復処理する必要があります。
  • Object はプロトタイプを持つため、既定のキーがマップ中に存在します。ES5ではこれを map = Object.create(null) を使うことで回避することができますが、推奨しません。
  • Map は、頻繁に要素を追加したり削除したりするシナリオでは、パフォーマンスがObject に比べて良い場合があります。

そのほか、オブジェクトだと JSON.stringfy() でJSON文字列に変換できません。やろうとすると次のようなコードになるのですが、オブジェクトのような形式にはならないです。

const map = new Map([
    ["a", 100],
    ["b", 200],
    ["c", 300],
]);

const json = JSON.stringify([...map])
// [["a",100],["b",200],["c",300]]

以上。

参考URL

Javascriptカテゴリの最新記事