Just Chris

@jsborked

What You Should Know About ES6 Maps

JavaScript ES6 introduces a new data structure, called maps. Maps are designed as an alternative to using Object literals for storing key/value pairs that require unique keys, and provide very useful methods for iteration.

Object literals as “Maps”

Nothing is more fundamental in JavaScript than object literals. Creating a map of sorts is as simple as declaring it in code.

var map = {
"key1":"value1",
"key2":"value2"
}
map.key1; // == "value1"

This will work in many situations, but there are problems with using object literals as maps. We may have keys that clobber Object prototype methods.

var map = {
"key1":"value1",
"key2":"value2"
}
map.toString() == "[object Object]" // Correct
map.toString = "value3"
map.toString(); // Uncaught TypeError: map.toString is not a function

Another drawback of using object literals is all keys can only be strings. This works in many situations, but when attempting to use a primitive value as a string, the system will convert it to a string behind the scenes.

This kind of magic which happens under the hood and without notification to the user can cause unexpected results, for instance, if the provided key is an array.

map[[1,2,3]] = "value4" // the provided key is an array
map['1,2,3'] = "value4" // the system has made the key a string

Another problem with using object literals (pre-ES6), is property/key orders are not guaranteed. Just because you have added the keys in a certain order, does not mean they will remain in that order, when you iterate through the keys.

var o = {}
o.a = 1
o.b = 2
o.c = 3
for(key in o) console.log(key); 
// expected a,b,c - but not guaranteed to be in that order

Objects also lacks a forEach method. If you are used to iterating arrays using .forEach(), objects cannot be iterated in this way.

o.forEach // undefined

Maps in ES6

ES6 Maps provide a new object for this purpose. You create a new map by calling new Map([iterable]). Iterable can be an array or any iterable object whose elements are key/value pairs. The provided key/value pairs are added to the new map.

var m = new Map()
m.set('a', 1)
m.set('b', 2)
// {"a" => 1, "b" => 2}

Map Properties and Methods

Map provides a very convienent, .size property to get the size of the map. The size is also very convienently shown in the Chrome Dev console, along with the contents of the map.

var m = new Map()
m.set('a', 1)
m.set('b', 2)
m.size; // 2 Make sure to use .set so that size updates correctly
m; // Map(2) {"a" => 1, "b" => 2}

map.clear()

Clears the map of all entries.

map.delete(key)

Deletes a key and returns if the delete was successful.

var m = new Map()
m.set('a', 1)
m.set('b', 2)
m.delete('a'); // true
m.delete('c'); // false (key was not there to delete)

map.get(key), map.has(key)

Getting a key, and finding if a key is present.

m.get('a'); // 1
m.has('a'); // true
m.has('z'); // false

map.forEach(fn)

Provides a convienient way to iterate the map. You can get keys, values, and the map itself very easily.

var m = new Map()
m.set('a', 1)
m.set('b', 2)
m.forEach((k, v, m) => console.log(`key:${k} value:${v} map:${m}`))
// key:1 value:a map:[object Map]
// key:2 value:b map:[object Map]

for..of

Another option to iterate the map is to use for..of syntax, which can easily provide access to the keys and values of the map.

for([key,value] of m) 
console.log(key + '=' + value)
// a=1
// b=2

m.keys()

Returns a full-blown iterator, so you can iterate the keys one by one, on demand, using .next()

var m = new Map()
m.set('a', 1)
m.set('b', 2)
var iter = m.keys()
iter.next(); // Object {value: "a", done: false}
iter.next(); // Object {value: "b", done: false}
iter.next(); // Object {value: undefined, done: true}

m.values(), m.entries()

The map Values and Entries can be iterated in the same way. Entries will give you an array of the kind [key, value].

var iter = m.values()
iter.next(); // Object {value: 1, done: false}
iter.next(); // Object {value: 2, done: false}
iter.next(); // Object {value: undefined, done: true}
var iter = m.entries()
iter.next(); // Object {value: ["a", 1], done: false}
iter.next(); // Object {value: ["b", 2], done: false}
iter.next(); // Object {value: undefined, done: true}

Maps give you alot of control over operations that need to be performed on the keys, values, or entries.

2d Arrays to Maps

Maps can take 2d arrays in the constructor. Each array entry should have the format [key, value].

var arr = [['a', 1], ['b', 2]]
var m = new Map(arr)
m; // Map(2) {"a" => 1, "b" => 2}

Non-Strings as keys

You can use objects of all kinds as map keys. The system will not automatically convert the keys to strings as it does for object literals. This opens up a wide range of opportunities to do interesting things with maps. For instance, setting the document object as a key.

var m = new Map()
m.set(document, true) // uses the document object as a key
// Map(1) {#document {} => true}
m.get(document) 
// true

Maps are an important new feature of ES6, and can be used in a wide variety of use cases for storing key/value pairs.

More by Just Chris

Topics of interest

More Related Stories