Map Basics
A Map is an ordered collection of key-value pairs. Unlike a plain object, Map keys can be any value β including objects, functions, or primitives β and insertion order is preserved.
// Create a Map
const map = new Map();
// set(key, value) β add or update entries
map.set('name', 'Alice');
map.set(42, 'the answer');
map.set(true, 'boolean key');
const objKey = { id: 1 };
map.set(objKey, 'object as key'); // objects can be keys!
// get(key) β retrieve a value
console.log(map.get('name')); // 'Alice'
console.log(map.get(42)); // 'the answer'
console.log(map.get(objKey)); // 'object as key'
console.log(map.get('missing')); // undefined
// has(key) β check existence
console.log(map.has(true)); // true
console.log(map.has('absent')); // false
// size β number of entries (not .length)
console.log(map.size); // 4
// delete(key) β remove one entry
map.delete(42);
console.log(map.size); // 3
// clear() β remove all entries
// map.clear();
// Initialize from array of pairs
const roles = new Map([
['Alice', 'admin'],
['Bob', 'editor'],
['Carol', 'viewer']
]);
Iterating a Map
const scores = new Map([
['Alice', 95],
['Bob', 82],
['Carol', 88]
]);
// for...of with destructuring (most common)
for (const [name, score] of scores) {
console.log(`${name}: ${score}`);
}
// forEach
scores.forEach((value, key) => {
console.log(`${key} scored ${value}`);
});
// keys(), values(), entries()
console.log([...scores.keys()]); // ['Alice', 'Bob', 'Carol']
console.log([...scores.values()]); // [95, 82, 88]
console.log([...scores.entries()]); // [['Alice', 95], ['Bob', 82], ...]
// Convert Map β Array of objects
const arr = Array.from(scores, ([name, score]) => ({ name, score }));
// [{ name: 'Alice', score: 95 }, ...]
// Convert plain object β Map
const obj = { x: 1, y: 2 };
const mapFromObj = new Map(Object.entries(obj));
console.log(mapFromObj.get('x')); // 1
Alice: 95 Bob: 82 Carol: 88 Alice scored 95 ... ['Alice', 'Bob', 'Carol'] [95, 82, 88]
Map vs Object Comparison
| Feature | Map | Plain Object |
|---|---|---|
| Key types | Any value (objects, functions, primitives) | String or Symbol only |
| Key order | Insertion order guaranteed | Insertion order (mostly, ES2015+) |
| Size | map.size | Must compute with Object.keys().length |
| Prototype pollution | No inherited keys | Inherits from Object.prototype |
| Serialisation | Not directly JSON-serialisable | Works with JSON.stringify |
| Performance | Better for frequent add/remove | Better for static structures |
| Best for | Dynamic key-value stores, any-key-type | Structured data, configs, DTOs |
Set Basics
A Set stores a collection of unique values in insertion order. Adding an already-present value has no effect.
const set = new Set();
// add(value)
set.add(1);
set.add(2);
set.add(2); // duplicate β silently ignored
set.add('hello');
set.add({ id: 1 }); // different object reference β added
console.log(set.size); // 4
// has(value)
console.log(set.has(2)); // true
console.log(set.has('world')); // false
// delete(value)
set.delete(1);
console.log(set.size); // 3
// Initialize from array (automatic deduplication!)
const unique = new Set([1, 2, 2, 3, 3, 3, 4]);
console.log(unique.size); // 4
// Set deduplication pattern (most common use case)
const numbers = [1, 2, 2, 3, 3, 4];
const deduped = [...new Set(numbers)];
console.log(deduped); // [1, 2, 3, 4]
// Deduplicate strings
const tags = ['js', 'html', 'js', 'css', 'html'];
const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ['js', 'html', 'css']
Iterating a Set
const colors = new Set(['red', 'green', 'blue']);
// for...of (most common)
for (const color of colors) {
console.log(color);
}
// forEach
colors.forEach(color => console.log(color));
// Convert to Array
const arr = [...colors]; // spread
const arr2 = Array.from(colors); // Array.from
// Set operations (manual implementations)
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
// Union
const union = new Set([...a, ...b]);
console.log([...union]); // [1, 2, 3, 4, 5, 6]
// Intersection
const intersection = new Set([...a].filter(x => b.has(x)));
console.log([...intersection]); // [3, 4]
// Difference (a minus b)
const difference = new Set([...a].filter(x => !b.has(x)));
console.log([...difference]); // [1, 2]
WeakMap and WeakSet
The WeakMap and WeakSet variants hold weak references, meaning they do not prevent garbage collection of their keys/values. They are non-iterable and their primary use case is associating metadata with objects without creating memory leaks.
// WeakMap β keys must be objects; values can be anything
const cache = new WeakMap();
function process(obj) {
if (cache.has(obj)) {
return cache.get(obj); // return cached result
}
const result = /* expensive computation */ { computed: true };
cache.set(obj, result);
return result;
}
let domNode = document.getElementById('app');
cache.set(domNode, { clicks: 0 });
// If domNode is set to null and removed from DOM,
// the WeakMap entry is garbage-collected automatically
// WeakSet β stores objects (no primitives), weak references
const seen = new WeakSet();
function visitOnce(obj) {
if (seen.has(obj)) return 'already visited';
seen.add(obj);
return 'first visit';
}
// Key differences from Map/Set:
// - Keys/values must be objects (WeakMap keys, WeakSet values)
// - Not iterable (no forEach, no for...of, no .size)
// - Automatically cleaned up when references are lost
Use WeakMap when you want to attach private data or metadata to an object without interfering with garbage collection. Common patterns: caching DOM-related data, storing private instance data in class implementations, and memoisation caches keyed by object identity.
Use Set to deduplicate values or track membership. Use Map when you need non-string keys or need to frequently add/delete entries. Use a plain object for static, string-keyed structured data that needs JSON serialisation.
Map and Set: When Plain Objects Aren't Enough
ES6 added Map and Set for jobs objects and arrays do poorly. Map is a keyed collection where keys can be any type (objects, functions β not just strings); Set stores unique values.
| Object | Map | |
|---|---|---|
| Key types | string/symbol only | anything (objects too) |
| Size | Object.keys(o).length | map.size |
| Order | not guaranteed for int-like keys | insertion order |
| Iterate | needs Object.entries | directly iterable |
const m = new Map();
const key = { id: 1 };
m.set(key, "data"); // object as key β impossible with plain objects
m.set("count", 5);
m.get(key); m.size; m.has("count");
const unique = new Set([1, 1, 2, 3]); // {1, 2, 3}
[...new Set(arrayWithDupes)]; // dedupe idiom
Pick a Map when keys aren't strings, you add/remove often, or you need reliable ordering and a size count. Pick a Set for uniqueness and fast membership tests. Stick with a plain object for simple string-keyed records and when you need JSON serialization (Map doesn't stringify).
ποΈ Practical Exercise
Given an array of log entries where each entry is { userId, action, timestamp }:
- Use a
Mapto count how many actions each user performed. - Use a
Setto find the unique actions that were performed. - Log both results in a readable format.
π₯ Challenge Exercise
Implement a LRUCache class using a Map (which preserves insertion order). The cache should accept a capacity in the constructor and implement get(key) (returns -1 if not found, and moves the key to most-recently-used) and put(key, value) (evicts the least-recently-used item if capacity is exceeded).
π Summary
Mapstores ordered key-value pairs with any key type and a reliable.sizeproperty.- Prefer
Mapover objects when keys are dynamic, non-string, or frequently added/removed. Setstores unique values in insertion order; perfect for deduplication and membership tests.- Use
[...new Set(arr)]as a concise one-liner to remove array duplicates. - Set operations (union, intersection, difference) are easy with spread and
filter/has. WeakMapandWeakSethold weak references β they do not prevent garbage collection and are not iterable.- Use
WeakMapfor attaching private metadata to objects without memory leaks.
Interview Questions
- What is the difference between a Map and a plain object?
- How would you deduplicate an array using a Set?
- Why can't you iterate a WeakMap or WeakSet?
- How do you convert a Map to a plain object and back?
- Implement a Set intersection using the spread operator.
Related Topics
FAQ
Yes. Map iterates entries in the order they were inserted, which is part of the specification. Plain objects also follow insertion order for string keys in most modern engines, but this is not as strictly guaranteed for numeric-like keys.
Yes. Set uses the SameValueZero algorithm for equality. While NaN !== NaN in regular comparisons, a Set treats all NaN values as equal, so only one NaN can be stored.
A Map does not have prototype keys that can collide (e.g., 'constructor', 'toString'), supports any key type, and has a built-in size property. These make it safer and more predictable for dynamic key-value storage.
Use spread: [...mySet] or Array.from(mySet). Both produce a regular array in insertion order.
They are related conceptually β both allow weak references. WeakRef (ES2021) lets you hold a weak reference to any object and check if it has been garbage-collected, while WeakMap/WeakSet automatically clean up their entries.

