Map, Set and family
ShareThose data types are often forgotten, but they could be quite useful.
Map
Map can be used as replacement of plain object. It does offer more functional approach for manipulating data stored in Map instance and it really fits when we want to store data as associative array. Check out example below:
const map = new Map();
map.set('firstName', 'John');
map.set('lastName', 'Smith');
map.size; // -> 2
map.get('firstName'); // -> 'John'
map.entries(); // -> {"firstName" => "John", "lastName" => "Smith"}
map.clear();
map.size; // -> 0
Map allows to use any value as key in opposite of plain object, which all keys will be converted to string.
const objectKey = {};
map.set(objectKey, 5);
map.get(objectKey); // -> 5
Map can also be iterated like arrays (using Map forEach
function or standard for ... of
loop),
where plain object is a bit harder to iterate, because it may have not enumerable properties.
map.set('a', 'a');
map.set('b', 'b');
map.forEach((value, key) => console.log(key, value)); // -> 'a a', 'b b'
But Map also have their own "qualities" which may be problematic for some use cases.
For example Map cannot be serialized, so using it with JSON.stringify
won't work.
map.set('a', 'a');
JSON.stringify(map); // -> {}
There is a way to serialize Map, but it require to create special function called replacer
and pass it as a second argument to JSON.stringify
.
Example below:
const map = new Map();
map.set('a', 'a');
map.set('b', 'b');
function replacer(key, value) {
const originalObject = this[key];
if (originalObject instanceof Map) {
return [...originalObject.entries()].reduce((result, [key, value]) => {
return {
...result,
[key]: value,
};
}, {});
}
return value;
}
JSON.stringify(map); // -> {"a": "a", "b": "b"}
Set
Set is simpler data type, more similar to array than object. Similarly to Map it uses more functional interface for data manipulation and it's great when we want to create array with unique values only, because it won't allow duplicates.
const set = new Set();
set.add(1);
set.add(2);
set.add(1);
set.size; // -> 2
set.values(); // -> 1 => 1, 2 => 2
Unfortunately, situation with serialization of Set is the same as with Map.
WeakMap & WeakSet
Both of data typescan be used when we want to store data temporarily and give browser permission to clean unused data by GC when key (in case of WeakMap) or value (WeakSet) won't be referenced by anything else. So it's only useful when we want to store data as long as we need it.
WeakMap
WeakMap can only have keys created from plain object and it doesn't have methods to check size or iterate over WeakMap. But it's possible to get value by key.
const object = {};
let variable = object;
const map = new WeakMap();
map.add(object, 'value');
map.get(object); // -> "value"
variable = 1;
// After browser collected garbage
map.has(object); // -> false
WeakSet
WeakSet can only store objects and it doesn't provide methods to get stored data or check size. It's possible only to check if value exists in WeakSet.
const object = {};
let variable = object;
const set = new WeakSet();
set.add(object);
set.has(object); // -> true
variable = 1;
// After browser collected garbage
set.has(object); // -> false