Map
Map is a collection of keyed data items, just like an Object. But the main difference is that Map allows keys of any type.
Methods and properties are:
new Map()
– creates the map.map.set(key, value)
– stores the value by the key.map.get(key)
– returns the value by the key, undefined if key doesn’t exist in map.map.has(key)
– returns true if the key exists, false otherwise.map.delete(key)
– removes the value by the key.map.clear()
– removes everything from the map.map.size()
– returns the current element count.
For instance:
let map = new Map();
map.set('1', 'str1'); // a string key
map.set(1, 'num1'); // a numeric key
map.set(true, 'bool1'); // a boolean key
// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.size ); // 3
As we can see, unlike objects, keys are not converted to strings. Any type of key is possible.
Map can also use objects as keys.
For instance:
let john = { name: "John" };
// for every user, let's store their visits count
let visitsCountMap = new Map();
// john is the key for the map
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
Using objects as keys is one of the most notable and important Map features. The same does not count for Object. String as a key in Object is fine, but we can’t use another Object as a key in Object.
Let’s try:
let john = { name: "John" };
let ben = { name: "Ben" };
let visitsCountObj = {}; // try to use an object
visitsCountObj[ben] = 234; // try to use ben object as the key
visitsCountObj[john] = 123; // try to use john object as the key, ben object will get replaced
// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123
As visitsCountObj is an object, it converts all Object keys, such as john and ben above, to same string "[object Object]". Definitely not what we want.
Every map.set call returns the map itself, so we can “chain” the calls:
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
Iteration over Map
For looping over a map, there are 3 methods:
map.keys()
– returns an iterable for keys,map.values()
– returns an iterable for values,map.entries()
– returns an iterable for entries [key, value], it’s used by default in for..of.
For instance:
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// iterate over values (amounts)
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
alert(entry); // cucumber,500 (and so on)
}
The iteration goes in the same order as the values were inserted. Map preserves this order, unlike a regular Object.
Besides that, Map has a built-in forEach method, similar to Array:
// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 500 etc
});
Object.entries: Map from Object
When a Map is created, we can pass an array (or another iterable) with key/value pairs for initialization, like this:
// array of [key, value] pairs
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
alert( map.get('1') ); // str1
If we have a plain object, and we’d like to create a Map from it, then we can use built-in method Object.entries(obj) that returns an array of key/value pairs for an object exactly in that format.
So we can create a map from an object like this:
let obj = {
name: "John",
age: 30
};
let map = new Map(Object.entries(obj));
alert( map.get('name') ); // John
Here, Object.entries returns the array of key/value pairs: [ ["name","John"], ["age", 30] ]. That’s what Map needs.
Object.fromEntries: Object from Map
We’ve just seen how to create Map from a plain object with Object.entries(obj).
There’s Object.fromEntries method that does the reverse: given an array of [key, value] pairs, it creates an object from them:
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// now prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2
We can use Object.fromEntries to get a plain object from Map.
E.g. we store the data in a Map, but we need to pass it to a 3rd-party code that expects a plain object.
Here we go:
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
let obj = Object.fromEntries(map.entries()); // make a plain object (*)
// done!
// obj = { banana: 1, orange: 2, meat: 4 }
alert(obj.orange); // 2
A call to map.entries() returns an iterable of key/value pairs, exactly in the right format for Object.fromEntries.
We could also make line (*) shorter:
let obj = Object.fromEntries(map); // omit .entries()
That’s the same, because Object.fromEntries expects an iterable object as the argument. Not necessarily an array. And the standard iteration for map returns same key/value pairs as map.entries(). So we get a plain object with same key/values as the map.