JavaScript - Map & Set (WeakMap/WeakSet)
Overview
Estimated time: 35–45 minutes
Learn when and how to use Map
and Set
instead of plain objects or arrays, and how WeakMap
/WeakSet
enable memory-safe associations with objects.
Learning Objectives
- Choose between
Object
vsMap
, andArray
vsSet
. - Use
set
/get
/has
/delete
, iterate withfor..of
, and convert between structures. - Apply
WeakMap
/WeakSet
for caches and metadata without memory leaks.
Prerequisites
- JavaScript - Arrays
- JavaScript - Strings (recommended)
Map vs Object
- Map keeps insertion order, accepts keys of any type (objects, functions, primitives), has a
size
property, and offers easy iteration. - Object has a prototype chain by default, string/symbol keys only, and requires extra work for size and safe iteration.
// Creating and using a Map
const user = { id: 1 };
const m = new Map();
m.set('name', 'Ada');
m.set(user, { role: 'admin' });
m.get('name'); // 'Ada'
m.has(user); // true
m.size; // 2
m.delete('name'); // true
// Iterate (insertion order preserved)
for (const [k, v] of m) {
console.log(k, v);
}
// Build from pairs and convert back
const fromPairs = new Map([[1, 'one'], [2, 'two']]);
const pairs = Array.from(fromPairs); // [[1,'one'], [2,'two']]
// Object <-> Map bridges
const obj = { a: 1, b: 2 };
const mapFromObj = new Map(Object.entries(obj)); // [['a',1],['b',2]]
const objFromMap = Object.fromEntries(mapFromObj); // { a:1, b:2 }
Key equality: Map uses SameValueZero, so NaN
equals NaN
and -0
equals +0
.
Cloning and merging maps
const a = new Map([['x', 1]]);
const b = new Map([['y', 2]]);
// Clone
const clone = new Map(a);
// Merge (later entries override earlier ones)
const merged = new Map([...a, ...b, ['x', 99]]); // 'x' becomes 99
Set vs Array
- Set stores unique values, preserves insertion order, and offers fast membership checks with
has
. - Array allows duplicates and offers rich positional operations but slower membership checks for large data.
// Creating and using a Set
const s = new Set([1,2,2,3]); // {1,2,3}
s.add(4); // {1,2,3,4}
s.has(3); // true
s.delete(2); // {1,3,4}
s.size; // 3
// Iterate
for (const v of s) {
console.log(v);
}
// Convert between Set and Array
const unique = [...new Set([3,1,2,3,2,1])]; // [3,1,2]
const setFromArr = new Set([...'hello']); // {'h','e','l','l','o'}
Set operations
const A = new Set([1,2,3]);
const B = new Set([3,4]);
// Union
const union = new Set([...A, ...B]); // {1,2,3,4}
// Intersection
const intersection = new Set([...A].filter(x => B.has(x))); // {3}
// Difference
const difference = new Set([...A].filter(x => !B.has(x))); // {1,2}
WeakMap and WeakSet
- WeakMap: keys must be objects; values can be anything. Not iterable. Keys are held weakly: when the object is no longer referenced elsewhere, the entry can be garbage-collected.
- WeakSet: stores objects only, held weakly; not iterable.
// WeakMap for per-object metadata (avoids leaks)
const meta = new WeakMap();
function tag(obj, info) { meta.set(obj, info); }
function getTag(obj) { return meta.get(obj); }
let node = { kind: 'widget' };
tag(node, { cached: true });
getTag(node); // { cached: true }
// If 'node' goes out of scope and nothing else references it,
// the WeakMap entry can be collected.
node = null; // Later, GC may reclaim both the object and its metadata.
// WeakSet example
const seen = new WeakSet();
function visit(obj) {
if (seen.has(obj)) return; // already processed
seen.add(obj);
// ...process obj...
}
When to use which?
- Use Map for dynamic key-value stores, especially with non-string keys or frequent inserts/deletes.
- Use Object for simple, fixed-shape records and when integrating with JSON directly.
- Use Set to enforce uniqueness and for fast membership tests.
- Use WeakMap/WeakSet for associating data with objects without preventing garbage collection.
Common Pitfalls
- Iteration:
WeakMap
/WeakSet
are not iterable. You cannot spread or loop them; onlyset/get/has/delete
(andadd
forWeakSet
). - Serialization:
JSON.stringify
ignoresMap
/Set
. Convert withArray.from(map)
or[...set]
, orObject.fromEntries(map)
. - Key types:
WeakMap
keys must be objects; primitives throwTypeError
. - Cloning:
new Map(oldMap)
performs a shallow copy. Nested objects are shared.
Try it
Run to build a frequency map and unique list: