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.
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.
ποΈ 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).
Frequently Asked Questions
- Does a Map preserve insertion order?
- Yes.
Mapiterates 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. - Can a Set contain NaN?
- Yes.
Setuses the SameValueZero algorithm for equality. WhileNaN !== NaNin regular comparisons, a Set treats allNaNvalues as equal, so only oneNaNcan be stored. - Why use Map over an object for a dictionary?
- A
Mapdoes not have prototype keys that can collide (e.g.,'constructor','toString'), supports any key type, and has a built-insizeproperty. These make it safer and more predictable for dynamic key-value storage. - How do I convert a Set to an array?
- Use spread:
[...mySet]orArray.from(mySet). Both produce a regular array in insertion order. - Is WeakRef related to WeakMap?
- 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, whileWeakMap/WeakSetautomatically clean up their entries.
π 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.