Maxence Dalmais

@maxired

How to map a Map

2016 comes to an end, and ES6 is everywhere. But with ES6, came some new syntax and APIs. This article aims to answer to a very specific question : How to map a Map into an array ?

tl;dr: Just use Array.from(map, ([key, value]) => value)

If you are reading this, it means that you are interested in a more complete answer. To be clear, we are talking here about the JavaScript Map type. Instances of Map don’t provide a map methods. This article is about ways to implement one.

Still interested ? If so, we will first discuss about the need to do so, and mainly why you might prefer using a Map instead of a regular Object.

Then, we will discuss about different ways to do this mapping.

Map vs Object

Before going further, let’s try to answer a simple question : Why would anyone use Map in JavaScript ?

Coming from another language, such as say Java, using Map seems pretty obvious. But what if you come from a pre-ES6 world ? Any reason to use Map over Object ?

To be clear, I am not advocating in using Map everywhere. As always, it is the developer choice to decide what match best the current situation. To help you decide, I will list here few points for which you might prefer use Map instead of a plain Object.

Typed keys

Short Version : You can use any type as key for Map. You shall not use Object or Array, or any complex type properties of Other Object. Probably a better idea to use only string.

A regular JavaScript object, simply created with{} , is very flexible. For each keys, you can assign it any of other JavaScript types :

const obj = {}; 
obj.first = ‘myvalue’;
obj.arr = [];
obj.number = 2;
obj.deep = {};
obj.map = new Map();

Moreover, despite what you can read in some places, you can add some dynamic keys to your object :

const mykey = 'foo' + Math.random();
const obj = {};
obj[mykey] = 'bar';

Or even, using the new ES6 computed property syntax :

const mykey = 'foo' + Math.random();
const obj = {
[mykey]: 'bar'
}

The last three examples should proved you that regular JS object are very flexible. Still there is major difference :

Whereas Map can have keys of any type, keys from a JavaScript Object will be converted as String

This difference might be subtitle, but might lead to interesting bugs when not understood correctly

const map = new Map();
map.set(2, 'two');
map.set('2', 'TWO');
// Map {2 => "two", "2" => "TWO"}
const obj = {};
obj[2] = 'two';
obj['2'] = 'TWO';
obj;
// Object {2: "TWO"}

In that case, both behavior can be useful depending of the context, but now, lets try to guess what would happen if you try to assign an object :

const obj = {}
const key1 = { id: 'foo' }
const key2 = { id: 'bar' }
obj[key1] = 'KEY1';
obj[key2] = 'KEY2';
obj[key1];
// KEY2"

In the previous example, you assign to an object two different values to attributes provided by an object, but when you try to access the value of the first attribute, you get the value of the second. This might seem weird, but should be logical if you read carefully so far. As said before, object properties are converted to String before begin used. One could imagine, that the conversion is done using `JSON.stringify` or something similar, but the truth is much more simple: we simply do `new String(prop).toString()`

String(key1).toString() 
// "[object Object]"
String(key2).toString() 
// "[object Object]"
new String(key1).toString() === new String(key2).toString()

Given the previous example, it is now easy to understand why using complex types, such as Object, Arrays, or Map as a property name is not a good idea. As said, you could use a serialization layer before accessing your property, but this would give you a different behavior from map, if your key object is modified between the assignation and the access.

Built-in Methods and attribute

We have seen previously that if you need to use complex keys as properties names, you better use Map. But there are more reason it. One of them if the availability of built-in methods.

The most useful built-in attribute of the Map object is probably the size . It probably does not need further explanation, and simply return the current number of elements being in the map. It is probably pretty obvious why would someone prefer do map.size instead of Object.keys(obj).length .

Others instance methods, such as map.keys(), map.values() and map.entries() , are also very similar to what we could get with the corresponding static Object methods, but have a major difference: they return Iterators and not Arrays. I will not get into the details here, but I’ll come back to it in the second part of this article.

Finally, another method is provided as an helper function, which is the map.has(key) , and corresponding to the obj.hasOwnProperty(key) method.

Some more methods are available for Map. Details for most of them to be provided in the next section.

Using it has a shared references

Beside the Complex Object as Key feature, one of the reason to use a Map, would be to use it as a shared references across object, in a pure POO style.

This Map can be a Singleton, or simply shared across instances, but can be modified by them. This is here in total opposition with the everything Immutable trends of the JavaScript world, but again, everything is a matter of context.

Coming for the POO world, the map.get and map.set will feel very natural. Moreover, in the pure CRUD style, you probably want be able to remove a element from the map. Here you can just use the map.delete method, which you have to admit, feels much less hacky than delete obj.key

Finally, you also have the map.clear method, which allow you to reset your Map to the empty state.

Iteration

One of the most popular features of the Map object are the iteration features. Indeed, Map is coming with his new pal, the new for...of syntax.

The for...of syntax allow you to write code such as :

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
console.log(entry);
}
// [a, 1]
// [b, 2]
// [c, 3]

for (let [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
Extracted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of#Iterating_over_a_Map

Personally, I am not a big fan of those C style iterations, and prefer using higher order functions. Looks like I am not the only one, as this is also part of the famous Airbnb styleguide.

Different ways of Mapping a Map

No we got the basic of Map, we will now attempt to solve the initial question

for…of

If you liked the for...of syntax, you are probably not into the functional approach nor with the immutable one. If so, then you might just want use the following syntax to map a Map.

const iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);
const squareValues = [];
for (let [key, value] of iterable) {
squareValues.push(value * value);
}
squareValues;
// [1, 4, 9]
I would be really sad if you do so, since the goal of this whole article was for you to avoid using this syntax.

We have not described it before, but this syntax is possible because the Map is an Iterable object — such are the return of map.keys() and map.values().

forEach

On top of Map instance methods already described, there is another one that was not discussed so far: map.forEach

The forEach method provided here is really similar to the forEach method provided by arrays. Using the same syntax as you would do for Array, it is pretty easy to implement a map method.

const mapMap = function(map, mapFunction){

const toReturn = [];
map.forEach(function(value, key){ // be careful to the args order
toReturn.push(mapFunction(value, key));
})

return toReturn;
}

Using this syntax feels ok to me, but still show a loop and the mutation of the toReturn array. This would be ok in a utils files, but we probably don’t want pollute our code with this everywhere. The syntax I would aim for would be a pure native functional one .

Array.from

We recently discussed the fact the map method that we are aiming for is available on Array. Maybe a good solution would be to implement our Map to an Array is order to call the map method. Moreover, we also described that the Map is an Iterable.

Also since the ES6 specification, new static methods are now available on Array. One of them is theArray.from method. It allows to duplicate an an Array, convert an Array like object to an array, and also, convert an Iterable to an Array.

One can now write something like :

Array.from(p).map( ([key, value]) => value * value )

I think this start to feel pretty good. We are converting our Map to an Array, and then applying a map on top. What else could we wish ? Do this is in only one operation ? Good news, this is possible.

Indeed, the Array.from method can be called with a second optional argument, corresponding to a mapping method.

We can then do :

Array.from(p, ([key, value]) => value * value)

This is to me the best way to answer the initial question: ‘How to map a Map in`

Conclusion

There is sometimes where using a Map instead of an Object really make sense. When the case, there is still sometimes the need to apply a map method to transform a Map into a array of transformed object. When doing so, the best method to me is using the Array.from method with two parameters, one for the map, the second one for a mapping function.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.
To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Maxence Dalmais

Topics of interest

More Related Stories