JavaScript Map Data Structure
The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.
const map = new Map(); map.set("a", 1); map.set("b", 2); map.set("c", 3); console.log(map.get("a")); // Expected output: 1 map.set("a", 97); console.log(map.get("a")); // Expected output: 97 console.log(map.size); // Expected output: 3 map.delete("b"); console.log(map.size); // Expected output: 2
Map objects-- JavaScript maps are objects-- are collections of key-value pairs. A key in the Map may only occur once; it is unique in the Map's collection. A Map object is iterated by key-value pairs — a for...of
loop returns a 2-member array of [key, value] for each iteration. Iteration happens in insertion order, which corresponds to the order in which each key-value pair was first inserted into the map by the set()
method (that is, there wasn't a key with the same value already in the map when set()
was called).
The specification requires maps to be implemented that, on average, provide access times that are sublinear on the number of elements in the collection
. Therefore, it could be represented internally as a hash table (with O(1) lookup), a search tree (with O(log(N)) lookup), or any other data structure, as long as the complexity is better than O(N).
Construction of Maps
Maps can be constructed from arrays of key-value subarrays:
const kvArray = [ ["key1", "value1"], ["key2", "value2"], ]; // Use the regular Map constructor to transform a 2D key-value Array into a map const myMap = new Map(kvArray); console.log(myMap.get("key1")); // "value1" // Use Array.from() to transform a map into a 2D key-value Array console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray // A succinct way to do the same, using the spread syntax console.log([...myMap]); // Or use the keys() or values() iterators, and convert them to an array console.log(Array.from(myMap.keys())); // ["key1", "key2"]
Just like Arrays, Maps can be cloned:
const original = new Map([[1, "one"]]); const clone = new Map(original); console.log(clone.get(1)); // one console.log(original === clone); // false (useful for shallow comparison)
Note: Keep in mind that the data itself is not cloned. In other words, it is only a shallow copy of the Map.
Maps can be merged, maintaining key uniqueness:
const first = new Map([ [1, "one"], [2, "two"], [3, "three"], ]); const second = new Map([ [1, "uno"], [2, "dos"], ]); // Merge two maps. The last repeated key wins. // Spread syntax essentially converts a Map to an Array const merged = new Map([...first, ...second]); console.log(merged.get(1)); // uno console.log(merged.get(2)); // dos console.log(merged.get(3)); // three
Maps can be merged with Arrays, too:
const first = new Map([ [1, "one"], [2, "two"], [3, "three"], ]); const second = new Map([ [1, "uno"], [2, "dos"], ]); // Merge maps with an array. The last repeated key wins. const merged = new Map([...first, ...second, [1, "un"]]); console.log(merged.get(1)); // un console.log(merged.get(2)); // dos console.log(merged.get(3)); // three
Setting object properties
Setting Object properties works for Map objects as well, and can cause considerable confusion. Therefore, this appears to work in a way:
const wrongMap = new Map(); wrongMap["bla"] = "blaa"; wrongMap["bla2"] = "blaaa2"; console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
But that way of setting a property does not interact with the Map data structure. It uses the feature of the generic object. The value of 'bla' is not stored in the Map for queries. Other operations on the data fail:
wrongMap.has("bla"); // false wrongMap.delete("bla"); // false console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
The correct usage for storing data in the Map is through the set(key, value) method.
const contacts = new Map(); contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" }); contacts.has("Jessie"); // true contacts.get("Hilary"); // undefined contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" }); contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"} contacts.delete("Raymond"); // false contacts.delete("Jessie"); // true console.log(contacts.size); // 1
Iterating through Maps
Iterating Map with for...of
Maps can be iterated using a for...of
loop:
const myMap = new Map(); myMap.set(0, "zero"); myMap.set(1, "one"); for (const [key, value] of myMap) { console.log(`${key} = ${value}`); } // 0 = zero // 1 = one for (const key of myMap.keys()) { console.log(key); } // 0 // 1 for (const value of myMap.values()) { console.log(value); } // zero // one for (const [key, value] of myMap.entries()) { console.log(`${key} = ${value}`); } // 0 = zero // 1 = one
Iterating Map with forEach()
Maps can be iterated using the forEach()
method:
myMap.forEach((value, key) => { console.log(`${key} = ${value}`); }); // 0 = zero // 1 = one
Map.groupBy()
(ES2024)
The Map.groupBy()
method groups elements of an object according to string values returned from a callback function.
The Map.groupBy() method does not change the original object.
// Create an Array const fruits = [ {name:"apples", quantity:300}, {name:"bananas", quantity:500}, {name:"oranges", quantity:200}, {name:"kiwi", quantity:150} ]; // Callback function to Group Elements function myCallback({ quantity }) { return quantity > 200 ? "ok" : "low"; } // Group by Quantity const result = Map.groupBy(fruits, myCallback);
Other Map Methods
Method/Property | Meaning/Action |
---|---|
size | the number of elements in a map |
delete() | removes a map element, as in fruits.delete("apples"); |
clear() | removes all the elements from a map |
has(key) | returns true if a key exists in a map, as in fruits.has("apples"); |
entries() | returns an iterator object with the [key,values] in a map |
keys() | returns an iterator object with the keys in a map |
values() | returns an iterator object with the values in a map |