Latest

ESNext — Maps

This is the first post in our on-going series Introducing ESNext. We'll be discussing the Map data structure and its new support in ES6.

Maps

A map is a data structure which "maps" keys to values. In the Javascript world, we've been using Objects to represent this kind of data, like so:

{
    firstName: "Thomas",
    lastName: "Reynolds"
}

Another great use of this structure is for caches:

var cache = {};
cache[obj.id] = obj;
cache[obj.id] // => obj

But, there are quite a few weird Javascript issues with Objects and a major restriction on Object keys being non-objects.

Object keys are technically unordered. So if you define several in a row, then loop over the object, you aren't guaranteed to get the same order of keys.

Objects are part of the prototypal inheritance tree, which means the data can possibly inherit from another piece of data. In Javascript, this can change after you define your Object. Additionally, libraries which touch Object.prototype can add "keys" to your data. When you try to loop over your keys, you'll end up with unexpected keys. The safe way to avoid this in Javascript is by using hasOwnProperty:

for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
        // Do something with obj[key];
    }
}

Now, let's talk about how Maps address these issues. Maps don't allow the prototype tree to effect data. Creating one looks like this:

var cache = new Map();
cache.set(obj.id, obj);
cache.get(obj.id) // => obj

Maps also allow objects as keys:

var cacheParentToChildren = new Map();
cache.set(obj, obj.getChildren());
cache.get(obj) // => [...]

There are also functions for basic actions such as .values(), .keys(),.size() and .has(). I'll also use Maps short initialization parameter.

var person = new Map([
    'firstName', 'Thomas',
    'lastName',  'Reynolds'
]);

person.values() // => ['Thomas', 'Reynolds'] // And in order!
person.keys() // => ['firstName', 'lastName'] // And in order!
person.size() // => 2
person.has('firstName') => true

Lastly, let's get to loops. Array has had forEach forever, with Maps you can use similar code:

var populationMap = new Map([
    'cityA', 10000,
    'cityB', 100000,
    'cityC', 1000000
]);

populationMap.forEach(function(value, key) {
    console.log('The population of ' + key + ' is ' + value);
});

The old for loop has also gained new features for Maps:

for (var [key, value] of myMap) {
    console.log('The population of ' + key + ' is ' + value);
}

But, this has the old for loop issue with how var is scoped. ES6 introduced the let variable form to avoid it, but we'll talk about that later.

Hopefully, this will get you on the road to replacing many of your key/value Object data structures with Maps.

Related