JavaScript Sets and Maps Get Certified
Set and Map are built-in collection
types that solve common problems more cleanly than plain arrays
and objects. A Set stores unique values. A
Map stores key-value pairs where keys can be of any
type.
In this lesson you will learn how to create and use both, how they differ from arrays and objects, and when each is the right tool.
Creating a Set
A Set is created with new Set(). You can
pass an iterable (such as an array) to seed it with values.
Duplicates are automatically ignored.
What this code does: Creating a Set from an array automatically removes duplicates. The size reflects only the unique values.
const tags = new Set(["js", "css", "js", "html"]);
console.log(tags);
console.log(tags.size);
const tags = new Set(["js", "css", "js", "html"]); the second "js" is ignored automatically.console.log(tags); prints a Set containing only the three unique values.console.log(tags.size); returns 3 because duplicates were removed.Common Set Methods
add() inserts a value, has() checks for
existence, delete() removes a value, and
clear() empties the entire set.
What this code does: Use add, has, and delete
to manage membership. Adding an existing value has no effect.
const colors = new Set();
colors.add("red");
colors.add("blue");
colors.add("red"); // ignored
console.log(colors.has("blue"));
colors.delete("red");
console.log(colors);
const colors = new Set();colors.add("red");colors.add("blue");colors.add("red"); // ignored the Set still holds only two values.console.log(colors.has("blue")); returns true.colors.delete("red");console.log(colors); now contains only "blue".Iterating Over a Set
Sets are iterable. You can loop with for...of or use
forEach. Insertion order is preserved.
What this code does: Looping a Set with for...of visits each unique value
in the order it was added.
const cities = new Set(["Lisbon", "Porto", "Braga"]);
for (const city of cities) {
console.log(city);
}
const cities = new Set(["Lisbon", "Porto", "Braga"]);for (const city of cities) { Sets are directly iterable and yield values in insertion order.console.log(city);}Converting Between Set and Array
Use the spread operator or Array.from() to turn a
Set into an array (and vice versa).
What this code does: Spread converts a Set into an array so you can use array methods. The result is a regular array of the unique values.
const unique = new Set([1, 2, 2, 3]);
const asArray = [...unique];
console.log(asArray);
const unique = new Set([1, 2, 2, 3]);const asArray = [...unique]; the spread operator expands the Set into a new array.console.log(asArray); prints [1, 2, 3].What this code does: A common pattern for removing duplicates: wrap the array in a Set, then spread back into an array. Only unique ratings remain.
const submittedRatings = [5, 3, 5, 4, 3, 5, 2];
const uniqueRatings = [...new Set(submittedRatings)];
console.log(uniqueRatings);
const submittedRatings = [5, 3, 5, 4, 3, 5, 2];const uniqueRatings = [...new Set(submittedRatings)]; a common one-liner that keeps only unique values while preserving first-seen order.console.log(uniqueRatings); prints [5, 3, 4, 2].Creating a Map
A Map is created with new Map(),
optionally passing an array of [key, value] pairs.
What this code does: A Map stores key-value pairs. You can create one from an array of pairs.
Keys can be any type, not just strings.
const inventoryLevels = new Map([
["widgets", 120],
["gadgets", 45]
]);
console.log(inventoryLevels);
const inventoryLevels = new Map([["widgets", 120],["gadgets", 45]]);console.log(inventoryLevels); prints a Map with two entries.Common Map Methods
set() adds or updates a key-value pair,
get() retrieves a value, has() checks
for a key, and delete() removes a key-value pair.
What this code does: Use set, get, has, and delete
to manage entries. Missing keys return undefined from get.
const inventoryLevels = new Map();
inventoryLevels.set("widgets", 120);
inventoryLevels.set("gadgets", 45);
console.log(inventoryLevels.get("widgets"));
console.log(inventoryLevels.has("sprockets"));
inventoryLevels.delete("gadgets");
console.log(inventoryLevels);
const inventoryLevels = new Map();inventoryLevels.set("widgets", 120);inventoryLevels.set("gadgets", 45);console.log(inventoryLevels.get("widgets")); prints 120.console.log(inventoryLevels.has("sprockets")); returns false.inventoryLevels.delete("gadgets");console.log(inventoryLevels); now contains only the widgets entry.Map Keys Can Be Any Type
Unlike a plain object, whose keys are always converted to
strings, a Map can use objects, functions, or any
other value as a key without converting it.
What this code does: Map keys can be objects, not only strings. Each user object is used as a key so login times are stored per user instance.
const userAlice = { name: "Alice" };
const userBen = { name: "Ben" };
const lastLogin = new Map();
lastLogin.set(userAlice, "2026-09-01");
lastLogin.set(userBen, "2026-09-05");
console.log(lastLogin.get(userAlice));
const userAlice = { name: "Alice" };const userBen = { name: "Ben" };const lastLogin = new Map();lastLogin.set(userAlice, "2026-09-01");lastLogin.set(userBen, "2026-09-05");console.log(lastLogin.get(userAlice)); prints 2026-09-01. Object identity matters — a different object with the same contents would not match.Iterating Over a Map
A Map can be looped over directly with
for...of, and each iteration gives you a
[key, value] pair, which pairs naturally with array
destructuring.
What this code does: Looping a Map with for...of yields [key, value] pairs.
Each item and its quantity are printed in insertion order.
const inventoryLevels = new Map([
["widgets", 120],
["gadgets", 45],
["sprockets", 8]
]);
for (const [item, quantity] of inventoryLevels) {
console.log(item + ": " + quantity);
}
const inventoryLevels = new Map([["widgets", 120],["gadgets", 45],["sprockets", 8]]);for (const [item, quantity] of inventoryLevels) { each iteration yields a [key, value] pair that is destructured into item and quantity.console.log(item + ": " + quantity);}
You can also access just the keys, values, or entries with
keys(), values(), and
entries(), similar to Object.keys() and
friends.
Map vs. Object
| Aspect | Object | Map |
|---|---|---|
| Key types | Strings and symbols only | Any value, including objects and functions |
| Key order | Mostly insertion order, with some exceptions | Always insertion order |
| Size | Object.keys(obj).length | map.size (a direct property) |
| Iteration | Requires Object.entries() or similar | Directly iterable with for…of |
Set vs. Array
| Aspect | Array | Set |
|---|---|---|
| Duplicate values | Allowed | Automatically ignored |
| Access by index | Yes (arr[0]) | No direct index access |
| Checking existence | includes() (scans the array) | has() (generally faster for large collections) |
A Brief Note on WeakMap and WeakSet
JavaScript also provides WeakMap and
WeakSet, which behave similarly to
Map and Set, but only allow objects as
keys (or values, for WeakSet) and don’t prevent those
objects from being garbage collected when nothing else references
them.
What this code does: A WeakMap holds object keys without preventing garbage collection.
Metadata is attached to the session object and can be looked up while the session exists.
let session = { userId: 42 };
const sessionMetadata = new WeakMap();
sessionMetadata.set(session, { loginTime: Date.now() });
console.log(sessionMetadata.has(session));
let session = { userId: 42 };const sessionMetadata = new WeakMap(); WeakMap keys must be objects and do not prevent garbage collection.sessionMetadata.set(session, { loginTime: Date.now() });console.log(sessionMetadata.has(session)); returns true. If session later becomes unreachable, the entry can be collected automatically.
Because WeakMap and WeakSet aren’t
directly iterable and don’t have a size property,
they’re mainly used for specialized memory-management scenarios
rather than everyday data storage.
Practical Example
Let’s use a Set and a Map together to
track unique visitors and count how many times each one visited.
What this code does: Combine Set and Map: unique visitor count from a Set, and per-visitor visit counts from a Map built by iterating the same list.
const pageViews = ["userA", "userB", "userA", "userC", "userB", "userA"];
const uniqueVisitors = new Set(pageViews);
console.log("Unique visitors:", uniqueVisitors.size);
const visitCounts = new Map();
for (const visitor of pageViews) {
visitCounts.set(visitor, (visitCounts.get(visitor) || 0) + 1);
}
for (const [visitor, count] of visitCounts) {
console.log(visitor + " visited " + count + " time(s).");
}
const pageViews = ["userA", "userB", "userA", "userC", "userB", "userA"];const uniqueVisitors = new Set(pageViews);console.log("Unique visitors:", uniqueVisitors.size);const visitCounts = new Map();for (const visitor of pageViews) {visitCounts.set(visitor, (visitCounts.get(visitor) || 0) + 1);}for (const [visitor, count] of visitCounts) {console.log(visitor + " visited " + count + " time(s).");}Common Mistakes With Sets and Maps
- Trying to access a
Setelement by index, like an array, whenSetdoesn’t support index-based access. - Calling
.sizeas a function (.size()), when it’s actually a property, not a method. - Forgetting that object keys in a plain object are always converted to strings, unlike
Mapkeys. - Assuming a
Setautomatically sorts its values — it only preserves insertion order, not numeric or alphabetical order. - Using a
MaporSetwhen a simple array or object would be just as clear, adding unnecessary complexity for straightforward cases.
Best Practices for Sets and Maps
- Use a
Setwhen you need to guarantee uniqueness or frequently check whether a value already exists. - Use a
Mapwhen your keys aren’t simple strings, or when key order and frequent additions/removals matter. - Use the spread operator to convert between
Setand array when you need array-specific methods likemap()orfilter(). - Stick with plain objects for simple, string-keyed data that doesn’t need
Map‘s extra capabilities. - Reserve
WeakMapandWeakSetfor specialized cases involving memory management, rather than everyday data storage.
Quick Summary
- A
Setstores a collection of unique values, with no duplicates allowed. - A
Mapstores key-value pairs where keys can be any type, not just strings. - Both are directly iterable with
for...ofand preserve insertion order. Setis commonly used to remove duplicates from an array using the spread operator.Mapoffers asizeproperty and more predictable iteration compared to a plain object.WeakMapandWeakSetare specialized variants used mainly for memory-sensitive scenarios.
Try It Yourself
Modify the code below. Try adding a duplicate skill to see it get ignored, or adding a new key-value pair to the map.
What this code does: Practice example: a Set ignores the duplicate skill, and a Map stores skill levels. Try adding another skill or level, then click Test My Code.
const skills = new Set(["JavaScript", "CSS", "JavaScript", "HTML"]);
console.log(skills);
console.log(skills.size);
const skillLevels = new Map([
["JavaScript", "Advanced"],
["CSS", "Intermediate"]
]);
console.log(skillLevels.get("CSS"));
const skills = new Set(["JavaScript", "CSS", "JavaScript", "HTML"]);console.log(skills);console.log(skills.size); prints 3.const skillLevels = new Map([["JavaScript", "Advanced"],["CSS", "Intermediate"]]);console.log(skillLevels.get("CSS")); prints Intermediate.Tip: If you catch yourself writingif (!array.includes(value)) array.push(value)to avoid duplicates, that’s usually a sign aSetwould express the same intent more directly.
Understanding Set and Map is useful
before moving on to iterators, generators, and more advanced
JavaScript concepts.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.