Map and Set
Learn how to use Map and Set — two powerful ES6 data structures for storing unique values and key-value pairs.
Map and Set
JavaScript objects and arrays handle most data storage needs. But they have limitations in specific situations — objects only allow string keys, arrays allow duplicates. ES6 introduced two new data structures that solve these problems cleanly.
Map— like an object, but keys can be any typeSet— like an array, but every value must be unique
Map
A Map is a collection of key-value pairs where the keys can be any type — strings, numbers, objects, functions, even other Maps.
const map = new Map();
// Set values
map.set("name", "Ali");
map.set(1, "number key");
map.set(true, "boolean key");
const objKey = { id: 1 };
map.set(objKey, "object key");
// Get values
console.log(map.get("name")); // Ali
console.log(map.get(1)); // number key
console.log(map.get(true)); // boolean key
console.log(map.get(objKey)); // object keyCreating a Map
Three ways to create a Map:
// Empty Map
const map1 = new Map();
// From an array of [key, value] pairs
const map2 = new Map([
["name", "Ali"],
["age", 22],
["city", "Lahore"]
]);
// From Object.entries()
const user = { name: "Ali", age: 22 };
const map3 = new Map(Object.entries(user));
console.log(map2.get("name")); // Ali
console.log(map3.get("age")); // 22Map Methods
const scores = new Map([
["Ali", 88],
["Sara", 92],
["Zara", 76]
]);
// set — add or update
scores.set("Omar", 85);
scores.set("Ali", 95); // updates existing key
// get — retrieve a value
console.log(scores.get("Sara")); // 92
console.log(scores.get("Hassan")); // undefined — not found
// has — check if key exists
console.log(scores.has("Zara")); // true
console.log(scores.has("Hassan")); // false
// delete — remove a key
scores.delete("Zara");
console.log(scores.has("Zara")); // false
// size — number of entries
console.log(scores.size); // 3
// clear — remove all entries
// scores.clear();Iterating Over a Map
Maps are iterable. They maintain insertion order — unlike plain objects which may not.
const scores = new Map([
["Ali", 88],
["Sara", 92],
["Zara", 76]
]);
// for...of — gives [key, value] pairs
for (const [name, score] of scores) {
console.log(`${name}: ${score}`);
}
// Ali: 88
// Sara: 92
// Zara: 76
// Keys only
for (const name of scores.keys()) {
console.log(name); // Ali, Sara, Zara
}
// Values only
for (const score of scores.values()) {
console.log(score); // 88, 92, 76
}
// Entries — same as for...of
for (const [key, value] of scores.entries()) {
console.log(key, value);
}
// forEach
scores.forEach((score, name) => {
console.log(`${name}: ${score}`);
});Converting Between Map and Object
// Map to Object
const map = new Map([["name", "Ali"], ["age", 22]]);
const obj = Object.fromEntries(map);
console.log(obj); // { name: "Ali", age: 22 }
// Object to Map
const user = { name: "Ali", age: 22 };
const userMap = new Map(Object.entries(user));
console.log(userMap.get("name")); // AliMap vs Object — When to Use Which
// Object — simple key-value with string keys
const config = {
theme: "dark",
language: "en"
};
// Map — non-string keys, frequent additions/deletions, order matters
const elementData = new Map();
const btn = document.querySelector("button");
elementData.set(btn, { clicks: 0, lastClicked: null });| Object | Map | |
|---|---|---|
| Key types | Strings and Symbols only | Any type |
| Key order | Not guaranteed for all cases | Always insertion order |
| Size | Manual — Object.keys().length | .size property |
| Iteration | for...in, Object.keys() | for...of directly |
| Performance | Good for small static data | Better for frequent changes |
| Best for | Configuration, data records | Dynamic key-value storage |
Real Use — Caching
// Cache expensive function results
function createCache(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("Cache hit");
return cache.get(key);
}
console.log("Computing...");
const result = fn(...args);
cache.set(key, result);
return result;
};
}
function expensiveCalculation(n) {
// Simulate slow computation
return n * n * n;
}
const cachedCalc = createCache(expensiveCalculation);
console.log(cachedCalc(5)); // Computing... 125
console.log(cachedCalc(5)); // Cache hit — 125
console.log(cachedCalc(6)); // Computing... 216
console.log(cachedCalc(6)); // Cache hit — 216Real Use — Counting Occurrences
const words = ["apple", "banana", "apple", "cherry", "banana", "apple"];
const frequency = new Map();
for (const word of words) {
frequency.set(word, (frequency.get(word) ?? 0) + 1);
}
for (const [word, count] of frequency) {
console.log(`${word}: ${count}`);
}
// apple: 3
// banana: 2
// cherry: 1
// Sort by frequency
const sorted = [...frequency.entries()]
.sort((a, b) => b[1] - a[1]);
console.log(sorted);
// [["apple", 3], ["banana", 2], ["cherry", 1]]Set
A Set is a collection of unique values. Adding the same value twice has no effect — duplicates are simply ignored.
const set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(2); // duplicate — ignored
set.add(1); // duplicate — ignored
console.log(set); // Set { 1, 2, 3 }
console.log(set.size); // 3Creating a Set
// Empty Set
const set1 = new Set();
// From an array — duplicates removed automatically
const set2 = new Set([1, 2, 3, 2, 1, 4]);
console.log(set2); // Set { 1, 2, 3, 4 }
// From a string — unique characters
const chars = new Set("javascript");
console.log(chars); // Set { "j", "a", "v", "s", "c", "r", "i", "p", "t" }Set Methods
const fruits = new Set(["apple", "mango", "banana"]);
// add
fruits.add("grape");
fruits.add("apple"); // ignored — already exists
// has — check if value exists
console.log(fruits.has("mango")); // true
console.log(fruits.has("orange")); // false
// delete
fruits.delete("mango");
console.log(fruits.has("mango")); // false
// size
console.log(fruits.size); // 3
// clear — remove all
// fruits.clear();Iterating Over a Set
Sets are iterable and maintain insertion order.
const fruits = new Set(["apple", "mango", "banana", "grape"]);
// for...of
for (const fruit of fruits) {
console.log(fruit);
}
// apple, mango, banana, grape
// forEach
fruits.forEach(fruit => console.log(fruit));
// Spread to array
const arr = [...fruits];
console.log(arr); // ["apple", "mango", "banana", "grape"]
// Array.from
const arr2 = Array.from(fruits);The Most Common Use — Removing Duplicates
const numbers = [1, 2, 3, 2, 4, 1, 5, 3];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4, 5]One line. The cleanest way to deduplicate an array in JavaScript.
const tags = ["js", "css", "js", "html", "css", "react", "js"];
const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ["js", "css", "html", "react"]Set Operations — Union, Intersection, Difference
Sets make set theory operations clean:
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// Union — all values from both sets
const union = new Set([...setA, ...setB]);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]
// Intersection — values in both sets
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log([...intersection]); // [4, 5]
// Difference — values in A but not in B
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log([...difference]); // [1, 2, 3]
// Symmetric difference — values in either but not both
const symmetricDiff = new Set([
...[...setA].filter(x => !setB.has(x)),
...[...setB].filter(x => !setA.has(x))
]);
console.log([...symmetricDiff]); // [1, 2, 3, 6, 7, 8]Real Use — Tracking Unique Visitors
const visitedPages = new Set();
function trackVisit(page) {
const isNew = !visitedPages.has(page);
visitedPages.add(page);
if (isNew) {
console.log(`New page visited: ${page}`);
} else {
console.log(`Revisit: ${page}`);
}
}
trackVisit("/home"); // New page visited: /home
trackVisit("/about"); // New page visited: /about
trackVisit("/home"); // Revisit: /home
trackVisit("/contact"); // New page visited: /contact
console.log(`Total unique pages: ${visitedPages.size}`); // 3
console.log([...visitedPages]); // ["/home", "/about", "/contact"]Real Use — Permission System
const PERMISSIONS = {
admin: new Set(["read", "write", "delete", "manage-users", "settings"]),
editor: new Set(["read", "write"]),
viewer: new Set(["read"])
};
function hasPermission(role, permission) {
return PERMISSIONS[role]?.has(permission) ?? false;
}
function canAccess(role, requiredPermissions) {
return requiredPermissions.every(p => hasPermission(role, p));
}
console.log(hasPermission("admin", "delete")); // true
console.log(hasPermission("editor", "delete")); // false
console.log(hasPermission("viewer", "read")); // true
console.log(canAccess("admin", ["read", "write"])); // true
console.log(canAccess("editor", ["read", "delete"])); // falseMap and Set Together — A Real Example
// Track which users have seen which announcements
const announcements = new Map([
["ann-001", "New feature released!"],
["ann-002", "Scheduled maintenance tonight"],
["ann-003", "Welcome to the new dashboard"]
]);
const userSeenAnnouncements = new Map();
function markSeen(userId, announcementId) {
if (!userSeenAnnouncements.has(userId)) {
userSeenAnnouncements.set(userId, new Set());
}
userSeenAnnouncements.get(userId).add(announcementId);
}
function getUnseenAnnouncements(userId) {
const seen = userSeenAnnouncements.get(userId) ?? new Set();
return [...announcements.entries()]
.filter(([id]) => !seen.has(id))
.map(([id, text]) => ({ id, text }));
}
markSeen("user-1", "ann-001");
markSeen("user-1", "ann-003");
console.log(getUnseenAnnouncements("user-1"));
// [{ id: "ann-002", text: "Scheduled maintenance tonight" }]
console.log(getUnseenAnnouncements("user-2"));
// All three — user-2 has seen nothingMap stores the announcements. A Set inside the Map tracks which ones each user has seen. Clean, efficient, and no duplicates possible.
Summary
Map:
- Key-value pairs where keys can be any type
- Maintains insertion order
- Use
set(),get(),has(),delete(),size - Iterable directly with
for...of - Better than objects for dynamic keys, non-string keys, and frequent changes
Set:
- Collection of unique values only — duplicates ignored
- Maintains insertion order
- Use
add(),has(),delete(),size - Iterable directly with
for...of - Best use — removing duplicates from arrays with
[...new Set(array)] - Clean set operations — union, intersection, difference
When to use:
- Map over object — non-string keys, dynamic additions, order matters
- Set over array — uniqueness required, fast membership checks with
.has()