JavaScript Map and Set Reference
Complete guide to JavaScript Map and Set data structures — methods, properties, and practical usage patterns
Introduction
Map and Set are built-in JavaScript data structures introduced in ES6 that provide powerful alternatives to plain objects for specific use cases. Understanding when and how to use them is essential for writing efficient, clean code.
Map
A Map is a collection of key-value pairs where keys can be any type (objects, functions, primitives).
Creating a Map
// Empty Map
const map = new Map();
// From array of key-value pairs
const mapFromArray = new Map([
['key1', 'value1'],
['key2', 'value2'],
[42, 'the answer']
]);
// From another Map
const mapCopy = new Map(mapFromArray);
// Map with object keys
const userKey = { id: 1 };
const userMap = new Map([[userKey, { name: 'John' }]]);
Instance Methods
set(key, value)
Purpose: Adds or updates a key-value pair.
Returns: The Map instance (chainable)
const map = new Map();
// Add new key-value pairs
map.set('name', 'Alice');
map.set('age', 30);
// Chain multiple set calls
map
.set('city', 'Paris')
.set('country', 'France')
.set('language', 'French');
// Object keys
const obj = { id: 1 };
map.set(obj, 'value for object key');
// Function keys
function myFunc() {}
map.set(myFunc, 'value for function key');
get(key)
Purpose: Retrieves the value for a given key.
Returns: The value associated with the key, or undefined if not found
const map = new Map([['name', 'Bob'], ['age', 25]]);
console.log(map.get('name')); // 'Bob'
console.log(map.get('age')); // 25
console.log(map.get('nonexistent')); // undefined
// Object keys must be the same reference
const key = { id: 1 };
map.set(key, 'value');
console.log(map.get(key)); // 'value'
console.log(map.get({ id: 1 })); // undefined (different object)
has(key)
Purpose: Checks if a key exists in the Map.
Returns: Boolean
const map = new Map([['active', true]]);
console.log(map.has('active')); // true
console.log(map.has('inactive')); // false
// Check before getting
if (map.has('user')) {
const user = map.get('user');
// Process user
}
delete(key)
Purpose: Removes a key-value pair by key.
Returns: Boolean (true if key existed, false otherwise)
const map = new Map([['temp', 'data'], ['permanent', 'data']]);
console.log(map.delete('temp')); // true
console.log(map.delete('nonexistent')); // false
console.log(map.has('temp')); // false
// Conditional deletion
if (map.has('cache')) {
map.delete('cache');
}
clear()
Purpose: Removes all key-value pairs from the Map.
Returns: undefined
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
console.log(map.size); // 3
map.clear();
console.log(map.size); // 0
console.log(map.has('a')); // false
forEach(callback)
Purpose: Iterates through the Map in insertion order.
Callback Parameters: (value, key, map)
Returns: undefined
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
map.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// Output:
// a: 1
// b: 2
// c: 3
// With thisArg
const context = { multiplier: 10 };
map.forEach(function(value, key) {
console.log(`${key}: ${value * this.multiplier}`);
}, context);
Properties
size
Purpose: Returns the number of key-value pairs.
Returns: Number
const map = new Map();
console.log(map.size); // 0
map.set('a', 1).set('b', 2);
console.log(map.size); // 2
map.delete('a');
console.log(map.size); // 1
Iteration
Map is iterable and provides multiple iteration methods.
entries()
Purpose: Returns an iterator of [key, value] pairs.
Returns: MapIterator
const map = new Map([
['name', 'Charlie'],
['age', 35]
]);
for (const [key, value] of map.entries()) {
console.log(`${key}: ${value}`);
}
// entries() is the default iterator
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
keys()
Purpose: Returns an iterator of keys.
Returns: MapIterator
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
for (const key of map.keys()) {
console.log(key); // 'a', 'b', 'c'
}
// Convert to array
const keys = [...map.keys()]; // ['a', 'b', 'c']
values()
Purpose: Returns an iterator of values.
Returns: MapIterator
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
for (const value of map.values()) {
console.log(value); // 1, 2, 3
}
// Convert to array
const values = [...map.values()]; // [1, 2, 3]
Practical Map Patterns
Object Caching with WeakMap
// Cache expensive computations
const cache = new Map();
const expensiveOperation = (input) => {
if (cache.has(input)) {
return cache.get(input);
}
const result = computeExpensively(input);
cache.set(input, result);
return result;
};
Counting Occurrences
const words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const wordCount = new Map();
words.forEach(word => {
wordCount.set(word, (wordCount.get(word) || 0) + 1);
});
console.log(wordCount.get('apple')); // 3
console.log(wordCount.get('banana')); // 2
Grouping Data
const users = [
{ name: 'Alice', city: 'Paris' },
{ name: 'Bob', city: 'London' },
{ name: 'Charlie', city: 'Paris' }
];
const usersByCity = new Map();
users.forEach(user => {
const cityUsers = usersByCity.get(user.city) || [];
cityUsers.push(user);
usersByCity.set(user.city, cityUsers);
});
console.log(usersByCity.get('Paris'));
// [{ name: 'Alice', city: 'Paris' }, { name: 'Charlie', city: 'Paris' }]
Map vs Object
// Map advantages over plain objects:
// 1. Any type of key
const map = new Map();
const objKey = { id: 1 };
map.set(objKey, 'value'); // Works!
const obj = {};
obj[objKey] = 'value'; // Key converted to string "[object Object]"
// 2. Size property
console.log(map.size); // O(1) - instant
console.log(Object.keys(obj).length); // O(n) - must count
// 3. Better performance for frequent additions/removals
// Map is optimized for these operations
// 4. Iterable by default
for (const [key, value] of map) {
// Works out of the box
}
// 5. No prototype chain issues
map.set('toString', 'custom value'); // Safe
obj.toString = 'custom value'; // Breaks Object.prototype.toString
Set
A Set is a collection of unique values of any type.
Creating a Set
// Empty Set
const set = new Set();
// From array
const setFromArray = new Set([1, 2, 3, 3, 4, 4, 5]);
console.log(setFromArray.size); // 5 (duplicates removed)
// From string
const uniqueChars = new Set('hello');
console.log(uniqueChars.size); // 4 ('h', 'e', 'l', 'o')
// From another Set
const setCopy = new Set(setFromArray);
Instance Methods
add(value)
Purpose: Adds a value to the Set.
Returns: The Set instance (chainable)
const set = new Set();
// Add values
set.add(1);
set.add(2);
set.add(3);
// Chain add calls
set.add(4).add(5).add(6);
// Duplicate values are ignored
set.add(1);
set.add(1);
console.log(set.size); // 6 (not 8)
// Any type
set.add('string');
set.add({ key: 'value' });
set.add(function() {});
has(value)
Purpose: Checks if a value exists in the Set.
Returns: Boolean
const set = new Set(['apple', 'banana', 'orange']);
console.log(set.has('apple')); // true
console.log(set.has('grape')); // false
// Object references
const obj = { id: 1 };
set.add(obj);
console.log(set.has(obj)); // true
console.log(set.has({ id: 1 })); // false (different object)
delete(value)
Purpose: Removes a value from the Set.
Returns: Boolean (true if value existed, false otherwise)
const set = new Set(['a', 'b', 'c']);
console.log(set.delete('a')); // true
console.log(set.delete('a')); // false (already deleted)
console.log(set.has('a')); // false
clear()
Purpose: Removes all values from the Set.
Returns: undefined
const set = new Set([1, 2, 3]);
console.log(set.size); // 3
set.clear();
console.log(set.size); // 0
forEach(callback)
Purpose: Iterates through the Set in insertion order.
Callback Parameters: (value, value, set) - value appears twice for compatibility with Map
Returns: undefined
const set = new Set(['a', 'b', 'c']);
set.forEach((value) => {
console.log(value); // 'a', 'b', 'c'
});
// The second parameter is the same as the first
set.forEach((value1, value2) => {
console.log(value1 === value2); // true
});
Properties
size
Purpose: Returns the number of values in the Set.
Returns: Number
const set = new Set();
console.log(set.size); // 0
set.add(1).add(2).add(3);
console.log(set.size); // 3
set.delete(1);
console.log(set.size); // 2
Iteration
Set is iterable and provides multiple iteration methods.
entries()
Purpose: Returns an iterator of [value, value] pairs.
Returns: SetIterator
const set = new Set(['a', 'b', 'c']);
for (const [value1, value2] of set.entries()) {
console.log(value1 === value2); // true
console.log(value1); // 'a', 'b', 'c'
}
// entries() is the default iterator
for (const entry of set) {
console.log(entry); // ['a', 'a'], ['b', 'b'], ['c', 'c']
}
keys() and values()
Purpose: Both return an iterator of values (identical in Set).
Returns: SetIterator
const set = new Set([1, 2, 3]);
for (const value of set.keys()) {
console.log(value); // 1, 2, 3
}
for (const value of set.values()) {
console.log(value); // 1, 2, 3
}
// Convert to array
const values = [...set.values()]; // [1, 2, 3]
const keys = [...set.keys()]; // [1, 2, 3] (same as values)
Practical Set Patterns
Remove Duplicates from Array
const arrayWithDuplicates = [1, 2, 2, 3, 3, 4, 5, 5];
const uniqueArray = [...new Set(arrayWithDuplicates)];
console.log(uniqueArray); // [1, 2, 3, 4, 5]
// For objects, use a Map or custom comparison
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' }
];
const uniqueUsers = [...new Map(users.map(u => [u.id, u])).values()];
Set Operations
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// Union: all elements from both sets
const union = new Set([...setA, ...setB]);
console.log(union); // {1, 2, 3, 4, 5, 6, 7, 8}
// Intersection: elements in both sets
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log(intersection); // {4, 5}
// Difference: elements in A but not in B
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log(difference); // {1, 2, 3}
// Symmetric Difference: elements in A or B but not both
const symmetricDifference = new Set(
[...setA].filter(x => !setB.has(x)).concat([...setB].filter(x => !setA.has(x)))
);
console.log(symmetricDifference); // {1, 2, 3, 6, 7, 8}
Tracking Visited Items
// BFS/DFS traversal
function traverse(node) {
const visited = new Set();
const queue = [node];
while (queue.length > 0) {
const current = queue.shift();
if (visited.has(current)) continue;
visited.add(current);
// Process current node
queue.push(...current.children);
}
}
// Track user interactions
const clickedButtons = new Set();
button.addEventListener('click', () => {
if (clickedButtons.has(button.id)) return;
clickedButtons.add(button.id);
// Handle click
});
Filtering Unique Values
// Filter array to keep only unique values
const items = ['a', 'b', 'a', 'c', 'b'];
const unique = [...new Set(items)]; // ['a', 'b', 'c']
// Filter based on property
const products = [
{ id: 1, category: 'electronics' },
{ id: 2, category: 'books' },
{ id: 3, category: 'electronics' }
];
const uniqueCategories = [...new Set(products.map(p => p.category))];
console.log(uniqueCategories); // ['electronics', 'books']
WeakSet
A WeakSet is a collection of unique objects with weak references.
const weakSet = new WeakSet();
const obj1 = { id: 1 };
const obj2 = { id: 2 };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
// Only objects allowed
// weakSet.add('string'); // TypeError
// No iteration methods (no keys(), values(), entries())
// No size property
// No clear() method
// Weak reference: objects can be garbage collected
obj1 = null; // obj1 can now be garbage collected from weakSet
Use Cases:
- Tracking objects without preventing garbage collection
- Marking objects as processed
- Preventing re-processing of DOM nodes
Performance Comparison
// Map vs Object for frequent additions/deletions
const map = new Map();
const obj = {};
// Map is faster for:
// - Frequent key additions/deletions
// - Large datasets
// - Non-string keys
// Object is faster for:
// - Simple key-value storage
// - JSON serialization
// - When you need prototype methods
// Set vs Array for existence checks
const array = [1, 2, 3, 4, 5];
const set = new Set([1, 2, 3, 4, 5]);
// O(n) - must iterate through array
array.includes(5);
// O(1) - hash table lookup
set.has(5); // Much faster for large collections
Summary Table
Map Methods
| Method | Purpose | Returns |
|---|---|---|
set(key, value) | Add/update key-value | Map (chainable) |
get(key) | Retrieve value | Value or undefined |
has(key) | Check key exists | Boolean |
delete(key) | Remove by key | Boolean |
clear() | Remove all | undefined |
forEach(cb) | Iterate | undefined |
entries() | [key, value] iterator | MapIterator |
keys() | Keys iterator | MapIterator |
values() | Values iterator | MapIterator |
size | Count (property) | Number |
Set Methods
| Method | Purpose | Returns |
|---|---|---|
add(value) | Add value | Set (chainable) |
has(value) | Check value exists | Boolean |
delete(value) | Remove value | Boolean |
clear() | Remove all | undefined |
forEach(cb) | Iterate | undefined |
entries() | [value, value] iterator | SetIterator |
keys() | Values iterator | SetIterator |
values() | Values iterator | SetIterator |
size | Count (property) | Number |
Conclusion
Map and Set are powerful additions to JavaScript:
Use Map when:
- You need non-string keys (objects, functions)
- You need to maintain insertion order
- You need frequent additions/removals
- You need size property (O(1) vs O(n))
Use Set when:
- You need unique values only
- You need fast existence checks (O(1) vs O(n))
- You need set operations (union, intersection, difference)
- You need to remove duplicates from arrays
Both provide better performance and cleaner APIs than plain objects or arrays for their specific use cases.
#javascript #data-structures #map #set #reference #web-development