JavaScriptで連想配列(辞書)というと、Object型とMap型の二つがあります。
今回は、動的に要素を追加削除するのに適したMap型について、使い方をまとめていきます。
Mapの作成
Mapはnew Map()
で作成します。
標準組み込みオブジェクトなので特別なimport等は不要です。
let myMap= new Map();
Map作成時に要素を設定することも可能です。
let myMap = new Map([
['key1', 'val1'],
['key2', 'val2']
]);
console.log(myMap); // Map(2) {"key1" => "val1", "key2" => "val2"}
要素の追加
要素の追加はset()
を使用します。
重複するキーがあった場合は後勝ち(上書き)の動作になります
let myMap= new Map();
// 要素の追加
myMap.set('key1', 'val1');
myMap.set('key2', 'val2');
console.log(myMap); // Map(2) {"key1" => "val1", "key2" => "val2"}
// キーが重複する要素を追加
myMap.set('key1', 'UPDATED');
// 後勝ちの動作になる
console.log(myMap); // Map(2) {"key1" => "UPDATED", "key2" => "val2"}
要素の取得
要素の取得はget()
を使用します。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
let x = myMap.get('key1');
console.log(x); // val1
一覧の取得
キーの一覧
配列を作成するArray.from()
にkeys()
を指定することでキーの一覧の配列を作成することができます。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
let keyArray = Array.from(myMap.keys());
console.log(keyArray); // ["key1", "key2"]
要素(バリュー)の一覧
配列を作成するArray.from()
にvalues()
を指定することでバリューの一覧の配列を作成することができます。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
let valueArray = Array.from(myMap.values());
console.log(valueArray); // ["val1", "val2"]
また、entries()
を指定することでキーバリューの一覧を作成することも可能です。
要素の判定(存在チェック)
has()
を使用して要素の存在をチェックすることができます。
存在しない要素に対してget()
を使用すると”undefined”が返るため、get()
で代用も可能です。
ただ、”undefined”判定の書き方で悩むぐらいならhas()
を使う方が健全だとは思います。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
// 要素の存在チェック
if (myMap.has('key2')) {
console.log('DEFINED');
}
// undefined判定で代用可能
if (typeof myMap.get('key3') === 'undefined') {
console.log('UNDEFINED')
}
要素数 (長さ)の取得
要素数の取得はsize
属性で取得します。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
// 要素数の取得
let size = myMap.size;
console.log(`size=${size}`); // size=2
Object型よりもコードがすっきりするので良いですね。
// Object型の場合
let myDict = { key1: 'hoge', key2: 'fuga', key3: 'piyo'}
let len = Object.keys(myDict).length
要素の削除
要素の削除はdelete()
を使用します。
返り値はtrue(削除成功)/false(削除失敗)です。
let myMap = new Map([['key1', 'val1'], ['key2', 'val2']]);
// 要素の削除
let returnValue = myMap.delete('key2');
console.log(returnValue); // true
// 削除に失敗した(キーが存在しない)場合はfalseが返る
returnValue = myMap.delete('key2');
console.log(returnValue); // false
ループ・繰り返し処理
ループ処理の書き方は何通りもあるかと思いますが、ここではよく使いそうな4パターンをご紹介します。
①・②:キー/バリューをループさせたい場合
③:キーのみループさせたい場合
④:バリューのみループさせたい場合
// ループ処理①:よくあるfor文の書き方
for (const [key, value] of myMap.entries()) {
console.log(`key=${key}, value=${value}`);
}
// ループ処理②:forEachを使った書き方 ★オススメ★
// パラメータ順が(value, key)なので注意
myMap.forEach((value, key) => {
console.log(`key=${key}, value=${value}`);
});
// ループ処理③:キーだけ回したい場合
for (const key of myMap.keys()) {
console.log(`key=${key}`);
}
// ループ処理④:バリューだけ回したい場合
for (const value of myMap.values()) {
console.log(`value=${value}`);
}
①と②は実現できることは同じですが、②の方が1.5倍ほど性能が良かったです。
(測定結果はこちら)
コーディング規約や趣味嗜好のしがらみがなければ②の書き方がおすすめです。
連想配列の結合(マージ)
連想配列を結合する場合は、結合元のMapを展開して新しいMapとして定義することで結合することができます。
要素追加と同じく、重複するキーは後勝ちの論理が働きます。
let myMap1 = new Map([['key1', 'val1'], ['key2', 'val2']]);
let myMap2 = new Map([['key1', 'new val1']]);
let myMap3 = new Map([['key3', 'val3'],['key4', 'val4']]);
// 連想配列の結合
let mergedMap1 = new Map([...myMap1, ...myMap2]); //...はスプレッド演算子で、要素の展開を行う
// 後勝ちなので、key1はmyMap2の値が残る
console.log(mergedMap1); // Map(2) {"key1" => "new val1", "key2" => "val2"}
// 3つ以上の連想配列の結合も可
let mergedMap2 = new Map([...myMap1, ...myMap2, ...myMap3]);
console.log(mergedMap2); // Map(4) {"key1" => "new val1", "key2" => "val2", "key3" => "val3", "key4" => "val4"}
// [key, value]の記法と組み合わせることも可
let mergedMap3 = new Map([...myMap1, ...myMap2, ['key5', 'val5'], ['key6', 'val6']]);
console.log(mergedMap3); // Map(4) {"key1" => "new val1", "key2" => "val2", "key5" => "val5", "key6" => "val6"}
参考
Mapについて
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map
スプレッド構文について
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax
undefined値の判定について
http://blog.tojiru.net/article/205007468.html
コメント