Map
Map オブジェクトはキーと値のペアを保持し、キーが最初に挿入された順序を覚えています。キーや値には任意の値(オブジェクトとプリミティブ値)を使用することができます。
試してみましょう
解説
Map オブジェクトは、キーと値のペアのコレクションです。Map のキーは一度しか出現しません。Map の集合の中で一意です。Map オブジェクトはキーと値のペアで反復処理されます。for...of ループは、各反復処理に対して [キー, 値] という 2 つのメンバーからなる配列を返します。反復処理は 挿入順 で行われます。これは、それぞれのキーと値のペアが set() メソッドによって最初にマップに挿入された順番に対応します(つまり、 set() が呼ばれたときには、すでに同じ値を持つキーがマップになかったということです)。
仕様書では、「平均して、集合の要素数に対してサブリニアなアクセス時刻を提供する」マップを実装することを要求しています。したがって、複雑度が O(N) よりも高い場合、内部的にはハッシュ表(O(1) ルックアップ)、探索木(O(log(N)) ルックアップ)、あるいは他のデータ構造として表すことが可能です。
キーの等価性
キー値の等価性は SameValueZero アルゴリズムに基づいて評価されます。(以前は SameValue が使われており、 0 と -0 が異なるものとして扱われていました。ブラウザーの互換性をチェックしてください)。これは NaN を NaN と等価と見なすもので(NaN !== NaN ですが)、他の値はすべて === 演算子の意味に従って等価性が考慮されます。
Object と Map の比較
Object と Map は似ています。どちらもキーを値に設定したり、それらの値を受け取ったり、キーを削除したり、キーに何かが格納されているかどうかを判定したりすることができます。この意味で(そして他の組み込みオブジェクトがなかったため)、従来 Object は Map として使われてきました。
しかし、いくつかの場面で Map の方が勝るような重要な違いがあります。
| Map | Object | |
|---|---|---|
| 偶発的なキー | Map は既定では何もキーを持っていません。明示的に設定したものだけを含みます。 |
メモ: ES5 では、
|
| セキュリティ | Map はユーザーが提供したキーと値を使用しても安全です。 |
ユーザーが提供したキーと値のペアを |
| キーの型 |
Map のキーはあらゆる値がなることができます
(関数、オブジェクト、あらゆるプリミティブなど)。
|
Object のキーは文字列またはシンボルでなければなりません。 |
| キーの順序 |
|
通常の
この順序は ECMAScript 2015 で初めて自身のプロパティに対してのみ定義されましたが、 ECMAScript 2020 では継承されたプロパティに対しても同様に順序が定義されています。
OrdinaryOwnPropertyKeys
と
EnumerateObjectProperties
の抽象指定操作を参照してください。しかし、オブジェクトのプロパティがすべて反復処理される単一の単一のメカニズムはないことに注意してください。
( |
|
大きさ |
Map の中のアイテム数は、 size プロパティで簡単に得ることができます。 |
Object 内のアイテムの数を決定することは、より回りくどく、効率的ではありません。一般的な方法は、 Object.keys() から返される配列の length を通じて行う方法です。 |
| 反復処理 | Map は 反復可能 ですので、直接反復処理を行うことができます。 |
Object
では反復処理を行うのに、いくつかの形でキーの一覧を取得して、そのうえで反復処理を行う必要があります。
メモ:
|
| 性能 |
キーと値のペアを頻繁に追加したり削除したりすることが求められる場面では、性能がより良くなります。 |
キーと値のペアを頻繁に追加したり削除したりすることに最適化されていません。 |
| シリアライズと解釈 |
シリアライズや解釈のためのネイティブな対応はありません。 (ただし、 replacer 引数で |
JSON から |
オブジェクトプロパティの設定
Map オブジェクトに対してオブジェクトプロパティを設定すると正しく動作しますが、混乱を催すことが考えられます。
たとえば、次の例は一応動作するように見えます。
js
const wrongMap = new Map();
wrongMap['bla'] = 'blaa';
wrongMap['bla2'] = 'blaaa2';
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
しかし、このようにプロパティを設定すると、 Map データ構造を使用しません。一般的なオブジェクトの機能を使用します。 'bla' の値はクエリーを行うための Map に格納されません。データにその他の操作を行うと失敗します。
js
wrongMap.has('bla') // false
wrongMap.delete('bla') // false
console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
Map にデータを格納する正しい方法は、 set(key, value) メソッドを使用する方法です。
js
const contacts = new Map()
contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})
contacts.has('Jessie') // true
contacts.get('Hilary') // undefined
contacts.set('Hilary', {phone: "617-555-4321", address: "321 S 2nd St"})
contacts.get('Jessie') // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete('Raymond') // false
contacts.delete('Jessie') // true
console.log(contacts.size) // 1
コンストラクター
Map()-
新しい
Mapオブジェクトを生成します。
静的プロパティ
get Map[@@species]-
派生クラスを生成するためのコンストラクター関数です。
インスタンスプロパティ
Map.prototype[@@toStringTag]-
@@toStringTagプロパティの初期値は文字列"Map"です。このプロパティはObject.prototype.toString()で使用されます。 Map.prototype.size-
Mapオブジェクトの中のキーと値のペアの数を返します。
インスタンスメソッド
Map.prototype.clear()-
Mapオブジェクトからすべてのキーと値のペアを削除します。 Map.prototype.delete()-
Mapオブジェクトに要素が存在し、削除された場合はtrueを返します。要素が存在しなければfalseを返します。その後ではMap.prototype.has(key)がfalseを返すようになります。 Map.prototype.get()-
keyで指定されたキーに結び付けられた値を返します。存在しない場合はundefinedを返します。 Map.prototype.has()-
論理値で、渡されたキーに結び付けられた要素が
Mapオブジェクト内に存在するかどうかを返します。 Map.prototype.set()-
Mapオブジェクト内のkeyで指定されたキーの値をvalueに設定します。そのMapオブジェクトを返します。 Map.prototype[@@iterator]()-
Mapオブジェクト内の各要素の[key, value] の配列が挿入順で含む、新しいイテレーターオブジェクトを返します。 Map.prototype.keys()-
Mapオブジェクト内の各要素のキーが挿入順で含む、新しいイテレーターオブジェクトを返します。 Map.prototype.values()-
Mapオブジェクト内の各要素の値が挿入順で含む、新しいイテレーターオブジェクトを返します。 Map.prototype.entries()-
Mapオブジェクト内の要素に対して挿入順にすべての要素の[key, value]の配列を含む、新しいイテレーターオブジェクトを返します。 Map.prototype.forEach()-
callbackFnを、Mapオブジェクトに存在するそれぞれのキーと値のペアに対して 1 回ずつ、挿入順に呼び出します。thisArg引数がforEachに与えられた場合は、それぞれのコールバックでこれをthisの値として使用します。
例
Map オブジェクトの使用
js
const myMap = new Map();
const keyString = '文字列'
const keyObj = {};
const keyFunc function() {};
// 値を設定する
myMap.set(keyString, "'文字列' と結び付けられた値");
myMap.set(keyObj, 'keyObj と結び付けられた値');
myMap.set(keyFunc, 'keyFunc と結び付けられた値');
console.log(myMap.size); // 3
// 値を取得する
console.log(myMap.get(keyString)); // "'文字列' と結び付けられた値"
console.log(myMap.get(keyObj)); // "keyObj と結び付けられた値"
console.log(myMap.get(keyFunc)); // "keyFunc と結び付けられた値"
console.log(myMap.get('a string')); // "'文字列' と結び付けられた値"。 keyString === '文字列' であるため
console.log(myMap.get({})); // undefined, keyObj !== {} であるため
console.log(myMap.get(function() {})); // undefined, keyFunc !== function () {} であるため
NaN を Map のキーとして使用
NaN もまたキーとして使うことができます。すべての NaN は自身と等しくない(NaN !== NaN は真)にもかかわらず、以下の例は動作します。これは NaN が互いに区別できないためです。
js
const myMap = new Map();
myMap.set(NaN, 'not a number');
myMap.get(NaN);
// "not a number"
const otherNaN = Number('foo');
myMap.get(otherNaN);
// "not a number"
for..of を使用した Map の反復処理
マップは for..of ループを使用して反復処理を行うことができます。
js
const myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// zero
// one
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
forEach() で Map を反復処理
マップは forEach() メソッドを使用して反復できます。
js
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
Array オブジェクトとの関係
js
const kvArray = [['キー1', '値1'], ['キー2', '値2']];
// 通常の Map コンストラクターを使って、キーと値の 2 次元配列をマップに変換する
const myMap = new Map(kvArray);
console.log(myMap.get('キー1')); // "値1" を返す
// 展開演算子を使って、マップをキー・値の 2 次元配列に変換する
console.log(Array.from(myMap)); // kvArray とまったく同じ Array を表示する
// あるいは展開演算子をキーまたは値のイテレーターに使って、キーまたは値のみの配列を得る
console.log([...myMap]);
// または keys() や values() のイテレーターを使用して配列に変換する
console.log(Array.from(myMap.keys())); // ["key1", "key2"] が出力される
Map の複製と混合
Array と同様に、 Map は複製することができます。
js
const original = new Map([
[1, 'one'],
]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false (useful for shallow comparison)
メモ: データ自身は複製されないことに注意しておいてください。
マップはキーの固有性を保持しながら混合可能です。
js
const first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
const second = new Map([
[1, 'uno'],
[2, 'dos'],
]);
// 2 つのマップを混合します。重複するキーは後勝ちになります。
// スプレッド演算子は基本的に Map を Array に変換します。
const merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Map は Array と混合することもできます。
js
const first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
const second = new Map([
[1, 'uno'],
[2, 'dos'],
]);
// マップと配列を混合します。重複するキーは後勝ちになります。
const merged = new Map([...first, ...second, [1, 'eins']]);
console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
仕様書
| Specification |
|---|
| ECMAScript Language Specification # sec-map-objects |
ブラウザーの互換性
BCD tables only load in the browser