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.

Here are some main operation you can do with a map:

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.

Chaining

Every map.set call returns the map itself, so we can “chain” the calls:

map.set('1', 'str1')
  .set(1, 'num1')
  .set(true, 'bool1');

Map from Object : Object.entries()

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.

Iteration over Map

For looping over a map, there are 3 methods:

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
});

Set

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:

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.

Iteration over Set

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:

keys(), values() and entries() for Object

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 -

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..inloop these methods for Object ignores the Symbol properties. If an Object has Symbol as key, it is not included in any of these methods.