Map and Set are ES6 collections that provide efficient alternatives to objects and arrays. Here's how to use them effectively.
Map Basics#
1// Create a Map
2const map = new Map();
3
4// Set values
5map.set('name', 'John');
6map.set('age', 30);
7map.set(123, 'number key');
8
9// Get values
10map.get('name'); // 'John'
11map.get('missing'); // undefined
12
13// Check existence
14map.has('name'); // true
15map.has('missing'); // false
16
17// Get size
18map.size; // 3
19
20// Delete
21map.delete('age'); // true (if existed)
22
23// Clear all
24map.clear();
25
26// Initialize with entries
27const userMap = new Map([
28 ['id', 1],
29 ['name', 'John'],
30 ['email', 'john@example.com']
31]);Map Keys#
1// Any value can be a key
2const map = new Map();
3
4// Object keys
5const objKey = { id: 1 };
6map.set(objKey, 'object value');
7map.get(objKey); // 'object value'
8
9// Function keys
10const fnKey = () => {};
11map.set(fnKey, 'function value');
12
13// DOM element keys
14const element = document.getElementById('app');
15map.set(element, { clicks: 0 });
16
17// Keys maintain reference equality
18const obj1 = { id: 1 };
19const obj2 = { id: 1 };
20map.set(obj1, 'first');
21map.set(obj2, 'second');
22map.get(obj1); // 'first'
23map.get(obj2); // 'second'
24map.size; // 2 (different references)
25
26// NaN as key (works unlike objects)
27map.set(NaN, 'not a number');
28map.get(NaN); // 'not a number'Map Iteration#
1const map = new Map([
2 ['a', 1],
3 ['b', 2],
4 ['c', 3]
5]);
6
7// for...of (entries)
8for (const [key, value] of map) {
9 console.log(key, value);
10}
11
12// forEach
13map.forEach((value, key) => {
14 console.log(key, value);
15});
16
17// Keys iterator
18for (const key of map.keys()) {
19 console.log(key);
20}
21
22// Values iterator
23for (const value of map.values()) {
24 console.log(value);
25}
26
27// Entries iterator
28for (const [key, value] of map.entries()) {
29 console.log(key, value);
30}
31
32// Convert to array
33const entries = [...map]; // [['a', 1], ['b', 2], ['c', 3]]
34const keys = [...map.keys()]; // ['a', 'b', 'c']
35const values = [...map.values()]; // [1, 2, 3]Map vs Object#
1// Map advantages over objects:
2
3// 1. Any key type
4const map = new Map();
5map.set({}, 'object key');
6map.set(42, 'number key');
7map.set(true, 'boolean key');
8
9// 2. Maintains insertion order
10const orderedMap = new Map([
11 ['first', 1],
12 ['second', 2],
13 ['third', 3]
14]);
15// Iteration order guaranteed
16
17// 3. Better size tracking
18map.size; // O(1) property
19// vs Object.keys(obj).length
20
21// 4. Better performance for frequent add/delete
22
23// 5. No prototype pollution
24const map2 = new Map();
25map2.get('toString'); // undefined
26// vs obj['toString'] returns function
27
28// When to use Object:
29// - JSON serialization needed
30// - Simple string keys
31// - Need object literal syntaxSet Basics#
1// Create a Set
2const set = new Set();
3
4// Add values
5set.add(1);
6set.add(2);
7set.add(3);
8set.add(2); // Duplicate, ignored
9
10// Check existence
11set.has(1); // true
12set.has(4); // false
13
14// Get size
15set.size; // 3
16
17// Delete
18set.delete(2); // true
19
20// Clear all
21set.clear();
22
23// Initialize with values
24const numbers = new Set([1, 2, 3, 2, 1]);
25// Set { 1, 2, 3 }
26
27// From string
28const chars = new Set('hello');
29// Set { 'h', 'e', 'l', 'o' }Set Operations#
1const setA = new Set([1, 2, 3, 4]);
2const setB = new Set([3, 4, 5, 6]);
3
4// Union
5const union = new Set([...setA, ...setB]);
6// Set { 1, 2, 3, 4, 5, 6 }
7
8// Intersection
9const intersection = new Set(
10 [...setA].filter(x => setB.has(x))
11);
12// Set { 3, 4 }
13
14// Difference (A - B)
15const difference = new Set(
16 [...setA].filter(x => !setB.has(x))
17);
18// Set { 1, 2 }
19
20// Symmetric difference
21const symmetricDiff = new Set(
22 [...setA].filter(x => !setB.has(x)).concat(
23 [...setB].filter(x => !setA.has(x))
24 )
25);
26// Set { 1, 2, 5, 6 }
27
28// Subset check
29const isSubset = [...setA].every(x => setB.has(x));
30
31// Superset check
32const isSuperset = [...setB].every(x => setA.has(x));Set Use Cases#
1// Remove duplicates from array
2const numbers = [1, 2, 2, 3, 3, 3, 4];
3const unique = [...new Set(numbers)];
4// [1, 2, 3, 4]
5
6// Remove duplicate objects (by property)
7const users = [
8 { id: 1, name: 'John' },
9 { id: 2, name: 'Jane' },
10 { id: 1, name: 'John' }
11];
12
13const uniqueUsers = [
14 ...new Map(users.map(u => [u.id, u])).values()
15];
16
17// Track unique values
18function countUniqueWords(text) {
19 const words = text.toLowerCase().match(/\w+/g) || [];
20 return new Set(words).size;
21}
22
23// Fast lookup
24const allowedIds = new Set([1, 2, 3, 4, 5]);
25
26function isAllowed(id) {
27 return allowedIds.has(id); // O(1) lookup
28}WeakMap#
1// Keys must be objects
2// Keys are weakly referenced (can be garbage collected)
3const weakMap = new WeakMap();
4
5let obj = { name: 'John' };
6weakMap.set(obj, 'metadata');
7
8weakMap.get(obj); // 'metadata'
9
10// When obj is garbage collected, entry is removed
11obj = null;
12
13// No iteration methods (keys can disappear anytime)
14// weakMap.keys() // Not available
15// weakMap.values() // Not available
16// weakMap.size // Not available
17
18// Use case: Private data
19const privateData = new WeakMap();
20
21class User {
22 constructor(name) {
23 privateData.set(this, { name });
24 }
25
26 getName() {
27 return privateData.get(this).name;
28 }
29}
30
31// Use case: Caching with automatic cleanup
32const cache = new WeakMap();
33
34function getCachedResult(obj) {
35 if (cache.has(obj)) {
36 return cache.get(obj);
37 }
38
39 const result = expensiveComputation(obj);
40 cache.set(obj, result);
41 return result;
42}WeakSet#
1// Values must be objects
2// Values are weakly referenced
3const weakSet = new WeakSet();
4
5let obj = { id: 1 };
6weakSet.add(obj);
7weakSet.has(obj); // true
8
9obj = null;
10// Entry will be garbage collected
11
12// Use case: Track objects without preventing GC
13const visitedNodes = new WeakSet();
14
15function traverse(node) {
16 if (visitedNodes.has(node)) {
17 return; // Already visited
18 }
19
20 visitedNodes.add(node);
21 // Process node
22 node.children.forEach(traverse);
23}
24
25// Use case: Mark objects
26const processed = new WeakSet();
27
28function processOnce(item) {
29 if (processed.has(item)) {
30 return;
31 }
32
33 processed.add(item);
34 // Process item
35}Practical Patterns#
1// Memoization with Map
2function memoize(fn) {
3 const cache = new Map();
4
5 return function(...args) {
6 const key = JSON.stringify(args);
7
8 if (cache.has(key)) {
9 return cache.get(key);
10 }
11
12 const result = fn.apply(this, args);
13 cache.set(key, result);
14 return result;
15 };
16}
17
18// Group by with Map
19function groupBy(array, keyFn) {
20 const map = new Map();
21
22 for (const item of array) {
23 const key = keyFn(item);
24 const group = map.get(key) || [];
25 group.push(item);
26 map.set(key, group);
27 }
28
29 return map;
30}
31
32const users = [
33 { name: 'John', role: 'admin' },
34 { name: 'Jane', role: 'user' },
35 { name: 'Bob', role: 'admin' }
36];
37
38const byRole = groupBy(users, u => u.role);
39// Map { 'admin' => [...], 'user' => [...] }
40
41// LRU Cache
42class LRUCache {
43 constructor(capacity) {
44 this.capacity = capacity;
45 this.cache = new Map();
46 }
47
48 get(key) {
49 if (!this.cache.has(key)) return -1;
50
51 const value = this.cache.get(key);
52 this.cache.delete(key);
53 this.cache.set(key, value);
54 return value;
55 }
56
57 put(key, value) {
58 this.cache.delete(key);
59 this.cache.set(key, value);
60
61 if (this.cache.size > this.capacity) {
62 const firstKey = this.cache.keys().next().value;
63 this.cache.delete(firstKey);
64 }
65 }
66}Converting Between Types#
1// Object to Map
2const obj = { a: 1, b: 2, c: 3 };
3const map = new Map(Object.entries(obj));
4
5// Map to Object
6const mapToObj = Object.fromEntries(map);
7
8// Array to Set
9const arr = [1, 2, 3];
10const set = new Set(arr);
11
12// Set to Array
13const setToArr = [...set];
14// or Array.from(set)
15
16// Map to Array of entries
17const mapToArr = [...map];
18
19// JSON with Map
20const mapJson = JSON.stringify([...map]);
21const mapFromJson = new Map(JSON.parse(mapJson));Best Practices#
Use Map When:
✓ Keys aren't strings/symbols
✓ Need to maintain insertion order
✓ Frequent additions/deletions
✓ Need map.size property
Use Set When:
✓ Need unique values only
✓ Fast membership testing
✓ Set operations needed
✓ Deduplicating arrays
Use WeakMap/WeakSet When:
✓ Keys are objects only
✓ Don't want to prevent GC
✓ Caching object metadata
✓ Private instance data
Avoid:
✗ Map for simple string keys
✗ Set for ordered unique lists
✗ WeakMap when you need iteration
✗ Converting unnecessarily
Conclusion#
Map and Set provide powerful alternatives to objects and arrays for specific use cases. Map excels with non-string keys and maintains insertion order, while Set ensures uniqueness and enables set operations. WeakMap and WeakSet are perfect for caching and tracking objects without preventing garbage collection. Choose the right collection based on your data structure needs.