Menu

JavaScript Map and Set: When to Use Them Over Objects and Arrays

How JavaScript's Map and Set work, how they differ from plain objects and arrays, and when reaching for them pays off.

Two Collections Beyond Object and Array

Plain objects and arrays cover most of what JavaScript programs need, but they weren't designed for every job. Map and Set are built-in collections that fill two specific gaps: keyed lookups where keys aren't strings, and deduplicated membership checks.

They've been in the language since ES2015. Both are iterable, both have a .size property, and both work well with the spread operator. The mental model is simple:

  • Map — like an object, but keys can be anything and order is preserved.
  • Set — like an array, but values are unique and lookup is fast.

Creating and Using a Map

A Map holds key/value pairs. You create one with new Map() and use .set(), .get(), .has(), and .delete():

index.js
Output
Click Run to see the output here.

You can also pass an array of [key, value] pairs to the constructor to seed it:

index.js
Output
Click Run to see the output here.

That two-element-array shape shows up everywhere Maps are involved — it's how entries are represented when you iterate.

Map vs Object: Why Bother?

Plain objects look like they do the same job. Most of the time they do. But Maps fix a few specific rough edges:

index.js
Output
Click Run to see the output here.

Objects inherit from Object.prototype, so keys like toString, constructor, and hasOwnProperty already exist on every object. Maps have no such baggage — the keys you set are the only keys that exist.

The other differences worth knowing:

  • Any key type. Maps accept objects, functions, numbers, booleans as keys. Objects silently convert non-string keys to strings: obj[1] and obj["1"] are the same slot.
  • Guaranteed insertion order. Maps iterate in the order entries were added. Objects mostly do too, but numeric-looking string keys get sorted first — a subtle trap.
  • Built-in size. map.size is O(1). For an object you'd write Object.keys(obj).length, which rebuilds an array.
  • Optimized for churn. Engines tune Maps for frequent add/remove. Objects are tuned for stable-shape records.

Use an object when you're modeling a record with known string keys ({ name, email, age }). Use a Map when keys are dynamic, not strings, or when you'll add and remove entries a lot.

Iterating a Map

Maps are iterable, which means for...of works directly and destructuring each entry is natural:

index.js
Output
Click Run to see the output here.

If you want just keys or just values, call .keys() or .values(). And .forEach() exists if you prefer it:

index.js
Output
Click Run to see the output here.

To turn a Map back into a plain object or array, spread it:

index.js
Output
Click Run to see the output here.

Creating and Using a Set

A Set holds unique values. Adding a value that's already there is a no-op:

index.js
Output
Click Run to see the output here.

Uniqueness is determined with the same equality rule as ===, with one quirk: NaN is considered equal to itself inside a Set, even though NaN === NaN is false everywhere else.

Pass an iterable to the constructor to seed a Set — this is where the dedupe trick comes from:

index.js
Output
Click Run to see the output here.

One line, any primitive type. For arrays of objects this doesn't work — two different objects with the same fields are still two different values — but for strings, numbers, and booleans it's the idiomatic deduplication.

Set vs Array: When to Switch

Arrays and Sets both hold a collection of values, so when do you pick which?

Reach for a Set when:

  • Values must be unique and you want the runtime to enforce it.
  • You're doing lots of membership checks. set.has(x) is O(1); array.includes(x) is O(n). Inside a loop, that difference compounds fast.
  • Order of insertion is all you need. Sets iterate in insertion order but don't support indexing.

Stick with an array when:

  • You need positional accessarr[0], slicing, sorting.
  • Duplicates are meaningful — a shopping cart with two of the same item.
  • You'll use array methods like .map, .filter, .reduce heavily. Sets don't have these; you'd spread into an array first.

A quick performance-shaped example:

index.js
Output
Click Run to see the output here.

If banned were an array, every filter callback would scan the whole list. As a Set, each lookup is constant time.

Iterating a Set

Same story as Map — for...of just works, and spreading gives you an array:

index.js
Output
Click Run to see the output here.

Sets also expose .keys(), .values(), and .entries() for symmetry with Map, even though for a Set keys and values are the same thing. Most of the time you'll just iterate directly.

A Worked Example: Counting Unique Visitors per Page

Combining both — a Map of page paths to a Set of visitor IDs:

index.js
Output
Click Run to see the output here.

The Map handles the path-to-bucket mapping; the Set handles the deduplication inside each bucket. Trying to do the same with a plain object and arrays would work, but you'd be writing extra indexOf checks and hasOwnProperty guards the whole way.

WeakMap and WeakSet, Briefly

Two related collections exist for a narrow use case: WeakMap and WeakSet. They hold references weakly, meaning an entry whose key (for WeakMap) or value (for WeakSet) has no other references gets garbage-collected automatically.

index.js
Output
Click Run to see the output here.

They only accept objects as keys, aren't iterable, and have no .size. That's by design — if you could iterate them, the garbage collector would be observable. They're useful for caching metadata about objects you don't own, and rare in day-to-day code.

Next: JSON

Map and Set are great in memory, but neither survives JSON.stringify intact — Maps become {} and Sets become {}. The next page covers JSON: how to serialize and parse data, and the patterns for handling the collections this page introduced when they need to cross a network or a file boundary.

Frequently Asked Questions

What's the difference between a Map and an Object in JavaScript?

A Map can use any value as a key — objects, functions, numbers, anything — while an object coerces keys to strings (or symbols). Map also tracks its size with .size, iterates in insertion order, and doesn't inherit keys from a prototype, so there's no risk of clashing with toString or constructor. Reach for Map when keys aren't strings or when you need frequent add/remove of entries.

What is a Set used for in JavaScript?

A Set stores unique values — it silently ignores duplicates. The fastest way to dedupe an array is [...new Set(arr)]. Sets also give you O(1) .has() checks, which beats array.includes() when you're testing membership inside a loop.

How do I iterate over a Map?

for...of works directly: for (const [key, value] of myMap) destructures each entry. You can also loop myMap.keys(), myMap.values(), or myMap.entries(). Iteration order is guaranteed to match insertion order, which plain objects don't always promise for numeric-looking keys.

Learn to code with Coddy

GET STARTED