Map
A Map is a keyed collection — like a plain object, but built from the ground up to be a dictionary. Its keys can be anything, not just strings. Its iteration order is the order keys were inserted. And the API distinguishes between "I want a value" and "I want to know if the key exists" without prototype-chain surprises. For anything resembling a lookup table, Map is almost always the right tool.
Creating a Map
const empty = new Map();
const fromEntries = new Map([
["alice", 90],
["bob", 85],
["carol", 75],
]);
console.log(fromEntries.get("bob")); // 8585
The constructor takes any iterable of two-element [key, value] arrays. That makes it round-trip nicely with Object.entries, fetch form data, and other Maps.
get, set, has, delete
map.set(key, value)— adds or updates. Returns the map (chainable).map.get(key)— returns the value, orundefinedif absent.map.has(key)— true/false. Use this whenundefinedis a valid value.map.delete(key)— removes; returnstrueif the key was there.map.clear()— empties.map.size— number of entries (a property, not a method).
const cache = new Map();
cache.set("user:1", { name: "Ada" })
.set("user:2", { name: "Lin" });
console.log(cache.has("user:1")); // true
console.log(cache.get("user:3")); // undefined
console.log(cache.size); // 2Any key type
Plain objects coerce their keys to strings — obj[1] and obj["1"] are the same property. Map keys keep their type. A function, a number, a DOM node and an object literal are all valid keys.
const m = new Map();
m.set(1, "number one");
m.set("1", "string one");
console.log(m.get(1)); // number one
console.log(m.get("1")); // string one
const key = {};
m.set(key, "by reference");
console.log(m.get(key)); // by reference
console.log(m.get({})); // undefined — different object referenceObject keys compare by reference, just like in Set. Two object literals with the same shape are different keys.
Iteration order
Iteration walks entries in insertion order, the same way Set does. Re-setting an existing key updates its value without changing its position.
const m = new Map([["a", 1], ["b", 2], ["c", 3]]);
m.set("b", 99); // updates b, but b stays in the middle
for (const [k, v] of m) console.log(k, v);a 1 b 99 c 3
Iterating
A Map is iterable: for...of yields [key, value] pairs. Three helper methods give you different views.
const m = new Map([["a", 1], ["b", 2]]); for (const [k, v] of m.entries()) console.log(k, v); for (const k of m.keys()) console.log(k); for (const v of m.values()) console.log(v); m.forEach((value, key) => console.log(key, "=", value));
forEach's callback is (value, key, map) — the same order as Array.forEach for consistency, which surprises people coming from other languages.
Map vs plain object
A plain object can do most of what a Map does, but Map wins almost every comparison:
Keys: Map accepts any type; objects coerce to strings (and symbols).
Inherited keys: Map is clean by construction. Objects inherit from
Object.prototype, sotoString,constructor,__proto__etc. exist as keys unless you useObject.create(null).Size:
map.sizeis O(1). For an object you have to countObject.keys(obj).length.Iteration: Maps are directly iterable. Objects require
Object.keys/entries/values.Performance: Map is optimised for frequent insertion and deletion. Objects are optimised for static shapes.
Converting between them
const obj = { a: 1, b: 2 };
const mapFromObj = new Map(Object.entries(obj));
const objFromMap = Object.fromEntries(mapFromObj);
console.log(mapFromObj); // Map(2) { 'a' => 1, 'b' => 2 }
console.log(objFromMap); // { a: 1, b: 2 }Common recipes
Counting occurrences
function count(values) {
const counts = new Map();
for (const v of values) {
counts.set(v, (counts.get(v) ?? 0) + 1);
}
return counts;
}
console.log(count(["a", "b", "a", "c", "a", "b"]));
// Map(3) { 'a' => 3, 'b' => 2, 'c' => 1 }Memoising a function
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}Indexing by id
const byId = new Map(users.map(u => [u.id, u])); console.log(byId.get(42)); // O(1) lookup
Gotchas
map[key]does not work the way you expect — it treats the map like an object and reads throughObject.prototype. Always useget/set.Maps are not JSON-serialisable out of the box.
JSON.stringify(new Map([["a", 1]]))returns"{}". Convert viaObject.fromEntries(map)for string keys.Equality is reference-based for objects. Two
{ id: 1 }literals are two different keys.