JavaScript by Example

Maps and Sets

Keyed collections that accept any value as a key (Map) and deduplicated collections (Set) - when to reach for them instead of plain objects and arrays.

Map is a keyed collection where keys can be any value, not just strings. Set is a collection of unique values where duplicates are ignored automatically. Plain objects coerce all keys to strings; arrays don't deduplicate. Both are built into modern JavaScript.

Map works like a dictionary. You add entries with .set(key, value), retrieve them with .get(key), and check for a key with .has(key). Iteration follows insertion order, which plain objects don't guarantee on all runtimes.

const scores = new Map();
 
scores.set("alice", 95);
scores.set("bob", 87);
scores.set("carol", 92);
 
console.log(scores.get("alice")); // 95
console.log(scores.has("bob"));   // true
console.log(scores.has("dave"));  // false
console.log(scores.size);         // 3
 
// Iteration order matches insertion order
for (const [name, score] of scores) {
  console.log(`${name}: ${score}`);
}
// alice: 95
// bob: 87
// carol: 92

Map accepts any value as a key, including objects, functions, and even NaN. A plain object coerces every key to a string, so using an object as a key silently collapses all such keys to "[object Object]".

const userCache = new Map();
 
const user1 = { id: 1, name: "Alice" };
const user2 = { id: 2, name: "Bob" };
 
userCache.set(user1, { lastSeen: "2026-04-27" });
userCache.set(user2, { lastSeen: "2026-04-26" });
 
console.log(userCache.get(user1)); // { lastSeen: "2026-04-27" }
console.log(userCache.get(user2)); // { lastSeen: "2026-04-26" }
 
// The same attempt on a plain object
const plainObj = {};
plainObj[user1] = "alice data";
plainObj[user2] = "bob data";
 
// Both keys stringify to "[object Object]" and clobber each other
console.log(Object.keys(plainObj)); // ["[object Object]"]
console.log(plainObj[user1]);       // "bob data" - wrong

Set stores unique values in insertion order. Adding a duplicate is silently ignored. Spreading a Set back into an array ([...new Set(arr)]) is the shortest idiom for deduplicating.

const tags = new Set();
 
tags.add("javascript");
tags.add("web");
tags.add("javascript"); // duplicate - ignored
tags.add("node");
 
console.log(tags.size);              // 3
console.log(tags.has("javascript")); // true
console.log(tags.has("python"));     // false
 
tags.delete("web");
console.log([...tags]); // ["javascript", "node"]
 
// One-liner dedup of an array
const nums = [1, 2, 3, 2, 1, 4];
const unique = [...new Set(nums)];
console.log(unique); // [1, 2, 3, 4]

In production

Reach for Map whenever keys aren't string literals or insertion order matters, and reach for Set whenever you'd otherwise write arr.includes(x) inside a loop (a linear scan each call). A Set.has() check is O(1). Plain objects also walk the prototype chain, so keys like toString or constructor collide with inherited properties - Map has no such issue.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free