Map is a collection of keyed data items, just like an Object. But the main difference is that Map allows keys of any type.
Here are some main operation you can do with a map:
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()
– clears the mapmap.size
– returns the current element count.Here is an example:
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 let map = new Map(); map.set('1', 'Santanu'); map.set(1, 'Kolkata'); console.log(map); // [["1","Santanu"],[1,"Kolkata"]] map.delete(1); console.log(map); // [["1","Santanu"]] // Use object as an Key - let obj = { 'key' : 'Value' }; map.set(obj, "Hi"); console.log(map); // [["1","Santanu"],[{"key":"Value"},"Hi"]] console.log(map.get(obj)); // Hi console.log(map.size); // 2
As we can see, unlike objects, keys are not converted to strings. Any type of key is possible. Using objects as keys is one of most notable and important Map features. To test values for equivalence, Map uses the algorithm SameValueZero
. It is roughly the same as strict equality ===
, but the difference is that NaN
is considered equal to NaN
. So NaN
can be used as the key as well.
Every map.set
call returns the map itself, so we can “chain” the calls:
map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1');
When a Map is created, we can pass an array (or another iterable) with key-value pairs, like this:
// array of [key, value] pairs let map = new Map([ ['1', 'str1'], [1, 'num1'], [true, 'bool1'] ]);
There is a built-in method Object.entries(obj)
for Object which converts the object and returns equivalent two dimensional array. For example -
let obj = { 'name' : "Santanu", 'location' : "Kolkata" }; console.log(Object.entries(obj)) // [ ['name', 'Santanu'], ['location' : 'Kolkata'] ];
So we can initialize a map from an object like this
let map = new Map(Object.entries({ name: "John", age: 30 })); console.log(map); // [ ["name","John"], ["age", 30] ]
Here, Object.entries returns the array of key/value pairs: [ ["name","John"], ["age", 30] ]
. That’s what Map needs.
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
.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) }
Besides that, Map has a built-in forEach
method, similar to Array:
recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc });
A Set is a collection of values, where each value may occur only once. Unlike array, it doesn't accept duplicate values.
Its main methods are:
new Set(iterable)
– creates the set, optionally from an array of values (any iterable will do).set.add(value)
– adds a value, returns the set itself.set.delete(value)
– removes the value, returns true if value existed at the moment of the call, otherwise false.set.has(value)
– returns true if the value exists in the set, otherwise false.set.clear()
– removes everything from the set.set.size
– is the elements count.let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // visits, some users come multiple times set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // set keeps only unique values alert( set.size ); // 3 for (let user of set) { alert(user.name); // John (then Pete and Mary) }
You can use array as an alternative to set. But array is slow. Because on each insertion, you need to check the whole array if the item is already exists. As array grows in size, the performance gets slower. On the other hand set is better optimized and perfectly suited for this case.
We can loop over a set either with for..of
or using forEach
:
let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // the same with forEach: set.forEach((value, valueAgain, set) => { alert(value); });
Note the funny thing. The forEach
function in the Set has 3 arguments: a value
, then again a value
, and then the target object. Indeed, the same value appears in the arguments twice.
That’s for compatibility with Map
where forEach
has three arguments. Looks a bit strange, for sure. But may help to replace Map
with Set
in certain cases with ease, and vice versa.
The same methods Map
has for iterators are also supported:
set.keys()
– returns an iterable object for values,set.values()
– same as set.keys
, for compatibility with Map
,set.entries()
– returns an iterable object for entries [value, value], exists for compatibility with Map
.Note that the methods keys()
, values()
and entries()
are well supported for Map
, Set
and Array
. For example -
let set = new Set(["oranges", "apples", "bananas"]); let values = set.values();
These methods return an Iterable, Not real Array. Object also supports these methods but in a different way. And also, for Object the returned value is an real Array. For Object -
Object.keys(obj)
– returns an array of keys.Object.values(obj)
– returns an array of values.Object.entries(obj)
– returns an array of [key, value] pairs.Note the difference that these methods for Object return “real” array objects, not just an iterable. That’s mainly for historical reasons.
let user = { name: "John", age: 30 }; // Object.keys(user) = ["name", "age"] // Object.values(user) = ["John", 30] // Object.entries(user) = [ ["name","John"], ["age",30] ]
Here’s an example of using Object.values
to loop over property values:
let user = { name: "John", age: 30 }; // loop over values for (let value of Object.values(user)) { alert(value); // John, then 30 }
Just like for..in
loop these methods for Object ignores the Symbol properties. If an Object has Symbol as key, it is not included in any of these methods.