They are the right data structures for caches, lookups, de-duplication and membership tests.
A Map stores key–value pairs where keys can be any value — objects, functions, numbers — not just strings:
const visits = new Map();
visits.set(user, 3);
visits.get(user); // 3Its API: set, get, has, delete, clear and the size property. Maps remember insertion order and are directly iterable: for (const [key, value] of map), plus keys(), values() and entries().
Prefer a Map over a plain object when keys are dynamic or user-provided, when you add and remove a lot, or when keys aren't strings. Objects have inherited properties ("toString" in {} is true) and string-only keys; Maps have neither problem. Convert with new Map(Object.entries(obj)) and Object.fromEntries(map). Map.groupBy(items, fn) (ES2024) builds a Map of groups.
A Set holds unique values. add, has, delete and size work as you'd expect; has is fast compared to array.includes on large collections. The quickest de-duplication is [...new Set(array)].
Both use SameValueZero equality: like ===, except NaN equals NaN. Objects are compared by reference, so two different { id: 1 } objects are two different keys.
ES2025 adds set algebra: a.union(b), a.intersection(b), a.difference(b), a.symmetricDifference(b), and isSubsetOf, isSupersetOf, isDisjointFrom. Each returns a new Set or a boolean.
WeakMap and WeakSet hold their object keys weakly, so entries vanish when the key object is garbage-collected — ideal for attaching metadata to objects you don't own.
Example
const ada = { name: "Ada" };
const roles = new Map([[ada, "admin"]]);
roles.set("guest-42", "viewer"); // keys of any type
console.log(roles.get(ada), roles.size); // admin 2
for (const [who, role] of roles) {
console.log(typeof who, role); // object admin / string viewer
}
const tags = ["js", "web", "js", "node", "web"];
const unique = [...new Set(tags)];
console.log(unique); // [ 'js', 'web', 'node' ]
const frontend = new Set(["js", "css", "html"]);
const backend = new Set(["js", "sql"]);
console.log(frontend.intersection(backend)); // Set(1) { 'js' }
console.log(frontend.union(backend).size); // 4
console.log(new Set([NaN, NaN]).size); // 1 (SameValueZero)
const counts = Object.fromEntries(new Map([["a", 1], ["b", 2]]));
console.log(counts); // { a: 1, b: 2 }Remember
- Map: any key type, insertion order,
size, directly iterable - Set: unique values, fast
has;[...new Set(arr)]de-duplicates - ES2025 sets have
union,intersection,differenceand friends
Watch out for
- Using objects as Map keys and expecting a different-but-equal object to find the entry.