Docs
6.3-Maps-and-Sets
6.3 Maps and Sets
Overview
ES6 introduced new built-in data structures: Map, Set, WeakMap, and WeakSet. These provide more efficient and specialized ways to store collections compared to plain objects and arrays.
Table of Contents
- ā¢Map
- ā¢Set
- ā¢WeakMap
- ā¢WeakSet
- ā¢Comparison with Objects and Arrays
- ā¢Use Cases and Patterns
Map
A Map holds key-value pairs where keys can be any type (including objects, functions, and primitives).
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Map ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Key (any type) āāāāāŗ Value (any type) ā
ā ā
ā āāāāāāāāāāā āāāāāāāāāāā ā
ā ā {obj} ā āāāāāāāāŗ ā "data" ā ā
ā āāāāāāāāāā⤠āāāāāāāāāā⤠ā
ā ā "name" ā āāāāāāāāŗ ā "Alice" ā ā
ā āāāāāāāāāā⤠āāāāāāāāāā⤠ā
ā ā 123 ā āāāāāāāāŗ ā [1,2,3] ā ā
ā āāāāāāāāāāā āāāāāāāāāāā ā
ā ā
ā ⢠Maintains insertion order ā
ā ⢠Keys can be ANY type ā
ā ⢠Has size property ā
ā ⢠Iterable by default ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Creating and Using Maps
// Creating a Map
const map = new Map();
// Setting values
map.set('name', 'Alice');
map.set(1, 'one');
map.set(true, 'boolean key');
// Object as key (impossible with plain objects!)
const objKey = { id: 1 };
map.set(objKey, 'object value');
// Getting values
console.log(map.get('name')); // 'Alice'
console.log(map.get(objKey)); // 'object value'
// Checking existence
console.log(map.has('name')); // true
console.log(map.size); // 4
// Deleting
map.delete('name');
// Clearing all
map.clear();
Map Initialization
// Initialize with array of key-value pairs
const map = new Map([
['name', 'Alice'],
['age', 30],
['city', 'NYC'],
]);
// From Object.entries()
const obj = { a: 1, b: 2, c: 3 };
const mapFromObj = new Map(Object.entries(obj));
// Convert Map back to Object
const backToObj = Object.fromEntries(map);
Iterating Maps
const map = new Map([
['a', 1],
['b', 2],
['c', 3],
]);
// for...of (default: entries)
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
// Keys only
for (const key of map.keys()) {
console.log(key);
}
// Values only
for (const value of map.values()) {
console.log(value);
}
// forEach
map.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
Set
A Set stores unique values of any type.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Set ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā ā
ā ā 1 ā 2 ā "a" ā {x} ā fn ā ā All unique values ā
ā āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā ā
ā ā
ā ⢠No duplicate values allowed ā
ā ⢠Maintains insertion order ā
ā ⢠Values can be any type ā
ā ⢠Fast lookup with has() ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Creating and Using Sets
// Creating a Set
const set = new Set();
// Adding values
set.add(1);
set.add(2);
set.add(2); // Duplicate - ignored!
set.add('hello');
set.add({ id: 1 });
console.log(set.size); // 4 (not 5!)
// Checking existence
console.log(set.has(1)); // true
console.log(set.has('hello')); // true
// Deleting
set.delete(1);
// Clearing
set.clear();
Set Initialization and Array Conversion
// Initialize with array
const set = new Set([1, 2, 3, 3, 4, 4, 5]);
console.log([...set]); // [1, 2, 3, 4, 5] - duplicates removed!
// Remove duplicates from array (common pattern!)
const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4]
// Convert Set to Array
const setToArray = Array.from(set);
Set Operations
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// Union
const union = new Set([...setA, ...setB]);
// Set {1, 2, 3, 4, 5, 6}
// Intersection
const intersection = new Set([...setA].filter((x) => setB.has(x)));
// Set {3, 4}
// Difference (A - B)
const difference = new Set([...setA].filter((x) => !setB.has(x)));
// Set {1, 2}
// Symmetric Difference
const symDiff = new Set([
...[...setA].filter((x) => !setB.has(x)),
...[...setB].filter((x) => !setA.has(x)),
]);
// Set {1, 2, 5, 6}
WeakMap
A WeakMap is a Map where keys must be objects and are held "weakly" (can be garbage collected).
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā WeakMap ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā ⢠Keys MUST be objects (not primitives) ā
ā ⢠Keys are weakly held (GC can collect them) ā
ā ⢠NOT iterable (no forEach, keys, values, entries) ā
ā ⢠No size property ā
ā ⢠Perfect for private data / metadata ā
ā ā
ā Only methods: get(), set(), has(), delete() ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
WeakMap Usage
const weakMap = new WeakMap();
let obj = { name: 'Alice' };
weakMap.set(obj, 'some private data');
console.log(weakMap.get(obj)); // 'some private data'
console.log(weakMap.has(obj)); // true
// When obj is no longer referenced elsewhere,
// both the key and value can be garbage collected
obj = null; // WeakMap entry becomes eligible for GC
// These DON'T work with WeakMap:
// weakMap.size ā
// weakMap.keys() ā
// weakMap.forEach() ā
Private Data Pattern with WeakMap
// Store private data for class instances
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
// Store password privately
privateData.set(this, { password });
}
checkPassword(pwd) {
return privateData.get(this).password === pwd;
}
}
const user = new User('Alice', 'secret123');
console.log(user.name); // 'Alice'
console.log(user.password); // undefined (private!)
console.log(user.checkPassword('secret123')); // true
WeakSet
A WeakSet is a Set where values must be objects and are held weakly.
const weakSet = new WeakSet();
let obj1 = { id: 1 };
let obj2 = { id: 2 };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
obj1 = null; // obj1 becomes eligible for GC
// Only methods: add(), has(), delete()
// NOT iterable, no size property
WeakSet Use Cases
// Track visited objects (e.g., deep clone)
const visited = new WeakSet();
function deepClone(obj) {
if (visited.has(obj)) {
throw new Error('Circular reference detected');
}
visited.add(obj);
// ... clone logic
}
// Mark objects as "processed"
const processed = new WeakSet();
function processOnce(obj) {
if (processed.has(obj)) return;
processed.add(obj);
// Process obj...
}
Comparison
| Feature | Object | Map | WeakMap |
|---|---|---|---|
| Key types | String/Symbol | Any | Objects only |
| Key order | Not guaranteed | Insertion order | N/A |
| Size | Manual count | .size | N/A |
| Iterable | Yes (keys) | Yes | No |
| GC of keys | No | No | Yes |
| Performance | Good | Better for frequent add/delete | Good |
| Feature | Array | Set | WeakSet |
|---|---|---|---|
| Duplicates | Allowed | Not allowed | Not allowed |
| Value types | Any | Any | Objects only |
| Order | Index-based | Insertion order | N/A |
| Lookup | O(n) | O(1) | O(1) |
| Iterable | Yes | Yes | No |
Use Cases
When to Use Map
// 1. Non-string keys needed
const userPermissions = new Map();
userPermissions.set(userObject, ['read', 'write']);
// 2. Frequent additions/deletions
const cache = new Map();
// 3. Need to maintain insertion order
const orderedConfig = new Map();
// 4. Need size property
console.log(map.size);
When to Use Set
// 1. Remove duplicates
const unique = [...new Set(array)];
// 2. Fast membership testing
const allowedIds = new Set([1, 2, 3, 4, 5]);
if (allowedIds.has(userId)) {
/* allowed */
}
// 3. Mathematical set operations
// Union, intersection, difference
When to Use WeakMap/WeakSet
// 1. Caching computed values for objects
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /* compute */;
cache.set(obj, result);
return result;
}
// 2. Storing private/metadata
// 3. Tracking object references without preventing GC
Summary
| Structure | Use When |
|---|---|
| Map | Need non-string keys, ordered pairs, or frequent add/remove |
| Set | Need unique values, fast lookup, or set operations |
| WeakMap | Need object keys with automatic cleanup |
| WeakSet | Need to track objects without preventing GC |
Next Steps
- ā¢Learn about TypedArrays for binary data (6.4)
- ā¢Explore Proxy and Reflect for metaprogramming (10.7)