Maps

The Map object is similar to dictionary in Python. It maps arbitrary values as keys to their values. The optional argument to the Map() constructor is an iterable object that yields two-element array [key, value].

let m = new Map(); // Empty map
let m2 = new Map([
  ['one', 1],
  ['two', 2]
]);  // A map with two elements
let mCopy = new Map(m2); // A map with elements copied from m2
let o = {x: 1, y: 2, z: 3}; // An object with three properties
let m3 = new Map(Object.entries(o)); // A map from object

You can query the value associated with a given key with the get() method and add a new key/value pair with the set() method. Multiple key/value pairs can be added to the map by chaining the set() method. The has() method checks whether a map includes the specified key, comparing the keys by identity ===. The delete() method removes the specified key and its associated value, while clear() removes all key/value pairs from the map. The size property returns the number of keys that a map contains.

m.set('one', 1); // Add the key 'one' to the value 1
m.set('two', 2); // Add another key/value pair
m.set('three', 3).set(false, 4); // Add multiple key/value pairs
m.size; // => 4
m.set('one', true); // Change the value associated with existing key
m.get('two'); // => 2
m.get(false); // => 4
m.get('four'); // => underfined
m.has('one'); // => true
m.has(true); // => false: the map does not have a key true
m.delete('one'); // => true
m.delete('four'); // => false: the key does not exist
m.size; // => 3
m.clear(); // Remove all keys and values from the map
m.size; // => 0

Map objects are iterable, and each iterated value is a two-element array, where the first element is a key and the second is the value associated with the key. The Map class iterates in insertion order, with the most recently added pair appear the last in the sequence.

for (let [key, value] of m3) {
  console.log(key, value);
}

The spread operator returns an array of arrays with key/value pairs. The keys() and values() methods allow you to iterate keys and values in insertion order.

[...m3.keys()]; // => ['x', 'y', 'z']
[...m3.values()]; // => [1, 2, 3]
[...m3]; // => [['x', 1], ['y', 2], ['z', 3]]
[...m3.entries()]; // => [['x', 1], ['y', 2], ['z', 3]]

The forEach() method iterates over map elements in a similar way like sets and arrays do. However, note that the arguments are (value, key) instead of (key, value) as in the for/of loop. You can think of a map as a generalized array in which integer indices are replaced with arbitrary key values.

m3.forEach((value, key) => {
  console.log(key, value);
});

A WeakMap in JavaScript is similar to a regular Map, but it has a few differences:

  1. Key Constraints: Unlike a regular Map, where keys can be of any type, WeakMap keys must be objects.
  2. Weak References: WeakMap holds "weak" references to the keys, meaning that if there are no other references to a key, it can be garbage collected, allowing the memory associated with it to be freed.

The intended use of WeakMap is to allow you to associate values with objects without causing memory leaks. For example, you can cache the result of computation on some object for later reuse by storing the result in WeakMap with the object as the key and the result as the value. Such an operation will not prevent the object from being garbage collected.

let myWeakMap = new WeakMap();
let key1 = {};
let key2 = {};

myWeakMap.set(key1, 'value associated with key1');
myWeakMap.set(key2, 'value associated with key2');

myWeakMap.get(key1); // => 'value associated with key1'
myWeakMap.has(key2); // => true