A practical overview of JavaScript Set and Map, including their core concepts, common APIs, real-world use cases, and the related WeakSet and WeakMap types.
My inner monologue after hearing this question:
I memorized all the impressive advanced topics, so why are we starting with the basics? I am interviewing for a senior role—shouldn't you ask about project tuning, first-screen performance, or build optimization? This interviewer is not following the script. All that fancy interview trivia I memorized is going to waste. Better luck next time.
Set and Map
Concepts
Why did ES6 introduce Set and Map? Why do languages such as Java also provide these structures and variations of them?
The official interview-style answer is:
ES6 added Set and Map to provide more powerful and flexible data operations for increasingly complex programming requirements.
What is a Set?
A Set is a collection data structure.
What is a Map?
A Map is a dictionary data structure.
What are collections and dictionaries?
A collection consists of unordered, related, non-duplicate values.
A dictionary consists of elements whose distinct
keyfields identify them.Both store unique entries. A Set conceptually stores
[value, value], while a dictionary stores[key, value].
Basic Usage
Initializing a Set
new Set()creates an empty Set.new Set(iterable)creates a Set from every value in an iterable.
Pay attention to the second form: the input must be iterable.
const set1 = new Set(); // Empty Set
const set2 = new Set([1, 2, 3]); // Three numeric values
const set3 = new Set("abc"); // Three characters
const set4 = new Set(
new Map([
["a", 1],
["b", 2],
]),
); // Two entry arraysUsing Set
The common operations are:
add(value): Add a value. Set usesadd, not an array'spush.delete(value): Delete a value.has(value): Return whether the value exists.clear(): Remove every member.
A Set's member count is its size, not length.
Set provides several iteration methods:
values(): Return an iterator over values.keys(): An alias ofvalues().entries(): Return an iterator over[value, value]pairs.forEach(): Invoke a callback for each member.for...of: Iterate over all members.
Examples matter more than reciting API names. Run them in a console until the behavior becomes familiar. AI may help us code day to day, but interviews still expect us to understand the fundamentals ourselves.
const set1 = new Set();
const set2 = new Set([1, 2, 3]);
const set3 = new Set("abc");
const set4 = new Set(
new Map([
["a", 1],
["b", 2],
]),
);
set2.forEach((value) => {
console.log(value);
});
for (const value of set2) {
console.log(value);
}
// values() returns an iterator.
const iterator1 = set3.values();
for (const value of iterator1) {
console.log(value);
}
// Each item returned by entries() is [value, value].
const iterator2 = set2.entries();
for (const entry of iterator2) {
console.log(entry);
}Initializing a Map
A Map is an ordered list of key-value pairs. Keys and values may be any type, including primitives and objects. Map is the constructor used to create it.
Characteristics:
- Key-value pairs: Both key and value can have any type.
- Insertion order: Iteration follows insertion order.
- Unique keys: Adding the same key again replaces its value.
- Efficient operations: Lookup, insertion, and deletion are generally efficient.
Use new Map() for an empty Map or new Map(iterable) to initialize it from key-value pairs.
const map1 = new Map();
const map2 = new Map([
["a", 1],
["b", 2],
]);
// A Set containing valid entry arrays is also iterable input for Map.
const map3 = new Map(
new Set([
["a", 1],
["b", 2],
]),
);Using Map
The familiar CRUD operations are:
map.set(key, value): Add or update a key-value pair. Map usesset, not Set'sadd.map.delete(key): Remove an entry.map.has(key): Check whether a key exists.map.get(key): Get the value for a key. Set has no correspondinggetmethod.
Like Set, Map exposes its entry count through size.
For iteration:
forEach()visits every key-value pair.for...ofiterates entries.keys()returns an iterator over keys.values()returns an iterator over values.entries()returns an iterator over key-value pairs.
const map1 = new Map([["a", 1]]);
const map2 = new Map([
["a", 1],
["b", 2],
]);
const map3 = new Map(
new Set([
["a", 1],
["b", 2],
]),
);
map1.forEach((value, key) => {
console.log(key, value);
});
for (const [key, value] of map2) {
console.log(key, value);
}
for (const key of map3.keys()) {
console.log(key);
}
for (const value of map3.values()) {
console.log(value);
}
for (const [key, value] of map3.entries()) {
console.log(key, value);
}Practice these examples in the console. There is no shame in typing them several times; repetition turns memorized facts into usable knowledge.
Common Set Use Cases
Removing Duplicates
A Set cannot contain duplicate values, which makes array deduplication concise:
[...new Set([1, 2, 3, 3, 1, 2])];Intersection
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
const intersection = new Set([...set1].filter((x) => set2.has(x)));Union
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
const union = new Set([...set1, ...set2]);Difference
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
const difference = new Set([...set1].filter((x) => !set2.has(x)));Equality
Compare sizes and ensure every value from one Set exists in the other:
const set1 = new Set([1, 2, 3]);
const set2 = new Set([1, 2, 3]);
console.log(set1.size === set2.size && [...set1].every((x) => set2.has(x))); // trueJavaScript Set does not provide a built-in equals() method.
Common Map Use Cases
Conversion
Map conversion is common and worth memorizing:
- Map to array:
[...map]orArray.from(map). - Array to Map:
new Map(iterable). - Map to object:
Object.fromEntries(map). - Object to Map:
new Map(Object.entries(obj)).
const arr = [...map1]; // [["a", 1]]
const map4 = new Map([
["a", 1],
["b", 2],
]);
const obj = Object.fromEntries(map4); // { a: 1, b: 2 }
const map5 = new Map(Object.entries(obj));Modeling a Dictionary
const dictionary = new Map([
["apple", "a fruit"],
["banana", "a fruit"],
["cat", "an animal"],
]);
console.log(dictionary.get("apple")); // "a fruit"Caching Data
const cache = new Map();
function getData(key) {
if (cache.has(key)) {
return cache.get(key);
}
const data = fetch(key);
cache.set(key, data);
return data;
}
const data = getData("https://www.example.com");Deduplicating Objects
Use a Map keyed by id to retain one object per identifier:
const objects = [
{ id: 1, name: "John" },
{ id: 2, name: "Mary" },
{ id: 1, name: "Alice" },
];
const uniqueObjects = new Map();
for (const object of objects) {
if (!uniqueObjects.has(object.id)) {
uniqueObjects.set(object.id, object);
}
}
console.log([...uniqueObjects.values()]);Counting Occurrences
const words = ["a", "b", "c", "a", "b"];
const wordCounts = new Map();
for (const word of words) {
if (wordCounts.has(word)) {
wordCounts.set(word, wordCounts.get(word) + 1);
} else {
wordCounts.set(word, 1);
}
}
console.log(wordCounts); // Map { "a" => 2, "b" => 2, "c" => 1 }Implementing an LRU Cache
Map's insertion order makes it useful for a least-recently-used cache:
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return undefined;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
if (this.cache.size === this.capacity) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
}
}
const cache = new LRUCache(2);
cache.set("a", 1);
cache.set("b", 2);
console.log(cache.get("a")); // 1
cache.set("c", 3);
console.log(cache.get("b")); // undefinedWeakSet and WeakMap
WeakSet stores weakly held objects. Referencing an object from a WeakSet does not prevent garbage collection. It is not enumerable, does not expose size, and supports add, delete, and has.
WeakMap stores key-value pairs whose keys are weakly held objects. A WeakMap reference does not prevent a key object from being collected. It is also not enumerable and supports set, delete, has, and get.
Both are useful for associating metadata with object lifetimes without accidentally keeping those objects alive.
Summary
Advanced topics cannot compensate for missing fundamentals. The more confidently you bluff, the harder it becomes to recover when an interviewer asks a basic question.
The dramatic inner monologues and interviewer commentary above are fictional—you know the style by now.
Hello, brother. Welcome to New World. I am the Elevator Warrior—leave a like and save the article, or I am coming for you!
Discussion
Support the author