Map

Map オブジェクトはキーと値の組を保持し、キーが最初に挿入された順序を覚えています。キーや値には任意の値 (オブジェクトとプリミティブ値)を使用することができます。

解説

Map オブジェクトは、その要素について挿入順で反復処理を行うことができます。 for...of ループは各反復で [キー, ] の配列を返します。

キーの等価性

  • キーの等価性は sameValueZero アルゴリズムに基づきます。
  • NaNNaN と同じとみなされ (NaN !== NaN であっても)、他の値はすべて === 演算子の意味に従って等価性が考慮されます。
  • 現在の ECMAScript 仕様書では、 -0+0 は等しいと考えられています。但し、以前の草稿ではそのようになっていませんでした。詳細は "Value equality for -0 and 0"ブラウザーの互換性一覧で確認してください。

Object と Map の比較

ObjectMap は似ています。 — どちらもキーを値に設定したり、それらの値を受け取ったり、キーを削除したり、キーに何かが格納されているかどうかを判定したりすることができます。この意味で (そして他の内蔵オブジェクトがなかったため)、従来 ObjectMap として使われてきました。

しかし、いくつかの場面で Map の方が勝るような重要な違いがあります。

Map Object
思いがけないキー Map は既定では何もキーを持っていません。明示的に設定したものだけを含みます。

Object にはプロトタイプがあります。よって既定のキーを含んでいるので、注意しないと自分のキーと衝突する可能性があります。

メモ: ES5 では、 Object.create(null) を使用すると回避できますが、これはめったに行われていません。

キーの型 Map のキーはあらゆる値がなることができます (関数、オブジェクト、あらゆるプリミティブなど)。 Object のキーは String または Symbol でなければなりません。
キーの順序

Map のキーは順序が守られます。すなわち、反復処理を行うと、 Map オブジェクトは挿入順でキーを返します。

Object のキーは順不同です。

メモ: ECMAScript 2015 以降、オブジェクトは文字列と Symbol のキーの作成順序を保存しています。 ECMAScript 2015 仕様書に従っている JavaScript エンジンでは、文字列のキーだけからなるオブジェクトでは、キーを挿入順に発生します。

大きさ Map の中の項目数は、 size プロパティで簡単に得ることができます。 property. Object の中の項目数は、手動で数える必要があります。
反復処理 Mapiterable ですので、直接反復処理を行うことができます。 Object では反復処理を行うのに、いくつかの形でキーの一覧を取得して、そのうえで反復処理を行う必要があります。
性能

キーと値の組を頻繁に追加したり削除したりすることが求められるシナリオでは、性能がより良くなります。

キーと値の組を頻繁に追加したり削除したりすることに最適化されていません。

コンストラクター

Map()
新しい Map オブジェクトを生成します。

プロパティ

Map.length
length プロパティの値は 0 です。
Map にいくつの項目があるかを数えるには、 Map.prototype.size を使用してください。
get Map[@@species]
派生クラスを生成するためのコンストラクター関数です。
Map.prototype
Map コンストラクターのプロトタイプを表します。すべての Map オブジェクトに追加のプロパティを定義できます。

Map インスタンス

すべての Map インスタンスは Map.prototype を継承しています。

プロパティ

Map.prototype.constructor
インスタンスのプロトタイプを生成した関数を返します。これは既定では Map 関数です。
Map.prototype.size
Map オブジェクトの中のキーと値の組の数を返します。

メソッド

Map.prototype.clear()
Mapオブジェクトからすべてのキーと値の組を削除します。
Map.prototype.delete(key)
Map オブジェクトに要素が存在し、削除された場合は true を返します、要素が存在しなければ false を返します。その後では Map.prototype.has(key)false を返すようになります。
Map.prototype.entries()
Mapオブジェクト内の要素に対して挿入順にすべての要素の [key, value] の配列を含む、新しい Iterator オブジェクトを返します。
Map.prototype.forEach(callbackFn[, thisArg])
callbackFn を、 Map オブジェクトに存在するそれぞれのキーと値の組に対して1回ずつ、挿入順に呼び出します。 thisArg 引数が forEach に与えられた場合は、それぞれのコールバックでこれを this の値として使用します。
Map.prototype.get(key)
key で指定されたキーに結び付けられた値を返します。存在しない場合は undefined を返します。
Map.prototype.has(key)
key で指定されたキーに結び付けられた要素が Map オブジェクト内に存在するかどうかを示す boolean を返します。
Map.prototype.keys()
新しい Iterator オブジェクトを返し、これには Mapオブジェクト内の各要素のキーが挿入順で含まれます。
Map.prototype.set(key, value)
Map オブジェクト内の key で指定されたキーの値を value に設定します。その Map オブジェクトを返します。
Map.prototype.values()
新しい Iterator オブジェクトを返し、これには Mapオブジェクト内の各要素のが挿入順で含まれます。
Map.prototype[@@iterator]()
新しい Iterator オブジェクトを返し、これには Mapオブジェクト内の各要素の [key, value] の配列が挿入順で含まれます。

Map オブジェクトの使用

let myMap = new Map()

let keyString = '文字列'
let keyObj    = {}
let keyFunc   = function() {}

// 値を設定する
myMap.set(keyString, "'文字列' と結び付けられた値")
myMap.set(keyObj, "keyObj と結び付けられた値")
myMap.set(keyFunc, "keyFunc と結び付けられた値")

myMap.size              // 3

// 値を取得する
myMap.get(keyString)    // "'文字列' と結び付けられた値"
myMap.get(keyObj)       // "keyObj と結び付けられた値"
myMap.get(keyFunc)      // "keyFunc と結び付けられた値"

myMap.get('文字列')      // "'文字列' と結び付けられた値"
                         // keyString === '文字列' であるため
myMap.get({})            // undefined, keyObj !== {} であるため
myMap.get(function() {}) // undefined, keyFunc !== function () {} であるため

NaNMap のキーとしての使用

NaN もまたキーとして使うことができます。すべての NaN は自身と等しくない (NaN !== NaN は真) にもかかわらず、以下の例は動作します。これは NaN が互いに区別できないためです。

let myMap = new Map()
myMap.set(NaN, 'not a number')

myMap.get(NaN) 
// "not a number"

let otherNaN = Number('foo')
myMap.get(otherNaN) 
// "not a number"

for..of を使用した Map の反復処理

Map は for..of ループを使用して反復処理を行うことができます。

let myMap = new Map()
myMap.set(0, 'zero')
myMap.set(1, 'one')

for (let [key, value] of myMap) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one

for (let key of myMap.keys()) {
  console.log(key)
}
// 0
// 1

for (let value of myMap.values()) {
  console.log(value)
}
// zero
// one

for (let [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one

forEach()Maps を反復処理

Map は forEach() メソッドを使用して反復できます。

myMap.forEach(function(value, key) {
  console.log(key + ' = ' + value)
})
// 0 = zero
// 1 = one

Array オブジェクトとの関係

let kvArray = [["キー1", "値1"], ["キー2", "値2"]];

// 通常の Map コンストラクターを使って、キー・値の 2 次元配列をマップに変換する
let myMap = new Map(kvArray)

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 は複製することができます。

let original = new Map([
  [1, 'one']
])

let clone = new Map(original)

console.log(clone.get(1))       // one
console.log(original === clone) // false (useful for shallow comparison)

重要: データ自身は複製されないことに注意しておいてください。

Map はキーの固有性を保持しながら混合可能です。

let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
])

let second = new Map([
  [1, 'uno'],
  [2, 'dos']
])

// 2つのマップを混合します。重複するキーは後勝ちになります。
// スプレッド演算子は基本的に Map を Array に変換します。
let 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 と混合することもできます。

let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
])

let second = new Map([
  [1, 'uno'],
  [2, 'dos']
])

// マップと配列を混合します。重複するキーは後勝ちになります。
let 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

使用上のメモ

ご注意ください。 Map オブジェクトに対するオブジェクトプロパティをの設定は正しく動作しますが、考えられる混乱を引き起こすことがあります。

従って、これは一応動作します・・・

let wrongMap = new Map()
wrongMap['bla'] = 'blaa'
wrongMap['bla2'] = 'blaaa2'

console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

・・・しかし、期待通りには動作しません。

wrongMap.has('bla')    // false
wrongMap.delete('bla') // false
console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

そして、正しい使い方との違いはわずかです。

let myMap = new Map()
myMap.set('bla','blaa')
myMap.set('bla2','blaa2')
console.log(myMap)  // Map { 'bla' => 'blaa', 'bla2' => 'blaa2' }

myMap.has('bla')    // true
myMap.delete('bla') // true
console.log(myMap)  // Map { 'bla2' => 'blaa2' }

仕様書

仕様書
ECMAScript (ECMA-262)
Map の定義

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
MapChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
完全対応 0.12
完全対応 0.10
無効
無効 From version 0.10: this feature is behind the --harmony runtime flag.
Map() constructorChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 あり
clearChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 19IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 19Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
deleteChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
完全対応 0.12
完全対応 0.10
無効
無効 From version 0.10: this feature is behind the --harmony runtime flag.
entriesChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 20IE 未対応 なしOpera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 20Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
forEachChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 25IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 25Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
getChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 あり
hasChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 あり
Key equality for -0 and 0Chrome 完全対応 38Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 25Safari 完全対応 9WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 29Opera Android 完全対応 25Safari iOS 完全対応 9Samsung Internet Android 完全対応 3.0nodejs 完全対応 4.0.0
keysChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 20IE 未対応 なしOpera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 20Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
setChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 13IE 部分対応 11
補足
部分対応 11
補足
補足 Returns 'undefined' instead of the 'Map' object.
Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 14Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 あり
sizeChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 19
補足
完全対応 19
補足
補足 From Firefox 13 to Firefox 18, the size property was implemented as a Map.prototype.size() method, this has been changed to a property in later versions conform to the ECMAScript 2015 specification.
IE 完全対応 11Opera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 19
補足
完全対応 19
補足
補足 From Firefox 13 to Firefox 18, the size property was implemented as a Map.prototype.size() method, this has been changed to a property in later versions conform to the ECMAScript 2015 specification.
Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
valuesChrome 完全対応 38Edge 完全対応 12Firefox 完全対応 20IE 未対応 なしOpera 完全対応 25Safari 完全対応 8WebView Android 完全対応 38Chrome Android 完全対応 38Firefox Android 完全対応 20Opera Android 完全対応 25Safari iOS 完全対応 8Samsung Internet Android 完全対応 3.0nodejs 完全対応 0.12
@@iteratorChrome 完全対応 43Edge 完全対応 12Firefox 完全対応 36
完全対応 36
未対応 27 — 36
補足 代替名
補足 A placeholder property named @@iterator is used.
代替名 非標準の名前 @@iterator を使用しています。
未対応 17 — 27
補足 代替名
補足 A placeholder property named iterator is used.
代替名 非標準の名前 iterator を使用しています。
IE 未対応 なしOpera 完全対応 30Safari 完全対応 10WebView Android 完全対応 43Chrome Android 完全対応 43Firefox Android 完全対応 36
完全対応 36
未対応 27 — 36
補足 代替名
補足 A placeholder property named @@iterator is used.
代替名 非標準の名前 @@iterator を使用しています。
未対応 17 — 27
補足 代替名
補足 A placeholder property named iterator is used.
代替名 非標準の名前 iterator を使用しています。
Opera Android 完全対応 30Safari iOS 完全対応 10Samsung Internet Android 完全対応 4.0nodejs 完全対応 0.12
@@speciesChrome 完全対応 51Edge 完全対応 13Firefox 完全対応 41IE 未対応 なしOpera 完全対応 38Safari 完全対応 10WebView Android 完全対応 51Chrome Android 完全対応 51Firefox Android 完全対応 41Opera Android 完全対応 41Safari iOS 完全対応 10Samsung Internet Android 完全対応 5.0nodejs 完全対応 6.5.0
完全対応 6.5.0
完全対応 6.0.0
無効
無効 From version 6.0.0: this feature is behind the --harmony runtime flag.
@@toStringTagChrome 完全対応 44Edge 完全対応 79Firefox 完全対応 51IE 未対応 なしOpera 未対応 なしSafari 未対応 なしWebView Android 完全対応 44Chrome Android 完全対応 44Firefox Android 完全対応 51Opera Android 未対応 なしSafari iOS 未対応 なしSamsung Internet Android 完全対応 4.0nodejs 未対応 なし

凡例

完全対応  
完全対応
部分対応  
部分対応
未対応  
未対応
実装ノートを参照してください。
実装ノートを参照してください。
ユーザーが明示的にこの機能を有効にしなければなりません。
ユーザーが明示的にこの機能を有効にしなければなりません。
非標準の名前を使用しています。
非標準の名前を使用しています。

関連情報