JavaScript developers live in objects and arrays. They handle most tasks well, but they're not the only tools available. Map and Set are two data structures built into JavaScript that solve problems objects and arrays handle awkwardly.
What Map Is
Map is a collection of key-value pairs, like an object. But unlike objects, Map accepts any type of key — strings, numbers, objects, functions, anything.
const userRoles = new Map();
userRoles.set('alice', 'admin');
userRoles.set('bob', 'editor');
userRoles.set('charlie', 'viewer');
console.log(userRoles.get('alice')); // 'admin'
console.log(userRoles.has('bob')); // true
console.log(userRoles.size); // 3The API is clean. .set() to add, .get() to retrieve, .has() to check existence, .size for count. No surprises about which method to use.
What Set Is
Set is a collection of unique values. Add a duplicate and it gets ignored — no error, no exception, just silently ignored.
const tags = new Set();
tags.add('javascript');
tags.add('nodejs');
tags.add('javascript'); // ignored — already exists
console.log(tags.has('nodejs')); // true
console.log(tags.size); // 2The uniqueness guarantee is built in. You don't need to check if something exists before adding it. Sets just handle that for you.
Why Objects Fall Short as Maps
Here's a problem with using plain objects for key-value storage: string coercion.
const map = {};
map[1] = 'one';
map['1'] = 'string one';
console.log(map[1]); // 'string one'
console.log(map['1']); // 'string one'The keys 1 and '1' are coerced to the same string, so the second assignment overwrites the first. With Map, numeric keys stay numeric:
const map = new Map();
map.set(1, 'one');
map.set('1', 'string one');
console.log(map.get(1)); // 'one'
console.log(map.get('1')); // 'string one'Objects also have prototype pollution risks and non-integer keys don't iterate predictably. Map sidesteps both — it has no prototype by default, and iteration follows insertion order.
Why Arrays Aren't Great for Unique Collections
Arrays let you add duplicates. If you need unique values, you're checking manually:
const items = ['a', 'b', 'a', 'c', 'b'];
// Getting unique values
const unique = items.filter((item, index) => items.indexOf(item) === index);
// or
const unique = [...new Set(items)];
console.log(unique); // ['a', 'b', 'c']This works, but it's a workaround. If you know uniqueness matters from the start, Set is the right tool:
const unique = new Set(['a', 'b', 'a', 'c', 'b']);
console.log([...unique]); // ['a', 'b', 'c']Sets also have O(1) lookup. Checking set.has(value) is fast regardless of size. For arrays, includes() scans the entire list.
Iteration and Methods
Map and Set are iterable, which means you can loop through them directly:
const fruitMap = new Map([
['apple', 5],
['banana', 3],
['mango', 2]
]);
for (const [fruit, count] of fruitMap) {
console.log(`\({fruit}: \){count}`);
}const colors = new Set(['red', 'green', 'blue']);
for (const color of colors) {
console.log(color);
}Both support .forEach() too, if you prefer that style.
When to Use Each
Use Map when:
You need key-value storage with non-string keys
You need predictable iteration order
You want a clean API without prototype pollution
Use Set when:
You need a collection of unique values
Fast membership checking matters
You're storing values that shouldn't repeat
Stick with objects when:
You're defining plain data structures with known properties
You're working with JSON serialization
The data is temporary or configuration-like
Stick with arrays when:
You need ordering with duplicates
You need indexed access by position
You're working with data that will serialize to JSON arrays
Wrapping Up
Map and Set aren't replacements for objects and arrays — they're complements. Map gives you flexible key-value storage with a clean API. Set gives you uniqueness guarantees without extra work. When you encounter a problem that objects or arrays handle awkwardly, reach for these. They'll make your code cleaner and your intent clearer.