Skip to main content

Command Palette

Search for a command to run...

Map and Set in JavaScript: A Practical Guide

Published
6 min read
Map and Set in JavaScript: A Practical Guide
S
Full-stack developer obsessed with performance, scalability, and clean systems. I use Arch btw.

Introduction

JavaScript gives you objects and arrays out of the box, and for a long time those two were the default answer to almost every data storage problem. Objects for key-value pairs, arrays for lists. Simple enough.

But both come with quirks that can bite you in real code. Map and Set were added in ES6 specifically to address those quirks. They're not replacements, but they solve certain problems cleaner than their older counterparts.


What Is a Map?

A Map is a collection of key-value pairs, similar to a plain object. The big difference is that a Map accepts any value as a key, not just strings and symbols.

const map = new Map();

map.set('name', 'Alice');
map.set(42, 'the answer');
map.set({ id: 1 }, 'an object key');

console.log(map.get('name')); // 'Alice'
console.log(map.get(42));     // 'the answer'
console.log(map.size);        // 3

Maps also maintain insertion order, iterate predictably, and give you a .size property without any tricks.


What Is a Set?

A Set is a collection of unique values. If you try to add a duplicate, it gets silently ignored. That's pretty much the core feature.

const set = new Set([1, 2, 3, 2, 1]);

console.log(set.size);          // 3
console.log([...set]);          // [1, 2, 3]

set.add(4);
set.delete(1);
console.log(set.has(1));        // false
console.log(set.has(4));        // true

Sets also maintain insertion order when you iterate over them.


Map vs Object: Where Objects Fall Short

Plain objects look like key-value stores, and they mostly work as one. But there are a few real-world gotchas.

Keys are coerced to strings

If you use a number or an object as a key on a plain object, JavaScript quietly converts it to a string. That can cause silent bugs.

const obj = {};
obj[1] = 'one';
obj['1'] = 'overwritten';

console.log(obj[1]); // 'overwritten'

With a Map, the key 1 (number) and '1' (string) are different keys.

Prototype pollution

A plain object comes with inherited properties from Object.prototype. That's usually harmless, but if you're checking for a key with 'toString' in obj, you'll get true even though you never set it.

No built-in size

Getting the number of keys in an object means calling Object.keys(obj).length, which creates a temporary array. A Map just has .size.

Here's a side-by-side:

Feature Object Map
Key types Strings and Symbols only Any value
Key ordering Not guaranteed (mostly) Insertion order
Size Object.keys(obj).length map.size
Prototype pollution Yes No
Iteration for...in, Object.entries() for...of, .forEach()
  • Use a plain object for structured data with known string keys, like a config or a DTO.

  • Use a Map when keys are dynamic, non-string, or when you need reliable iteration and size.


Set vs Array: The Duplicate Problem

Arrays are ordered and they allow duplicates. That's fine for most things. But if you have a list that should only contain unique values, an array leaves the enforcement to you.

Say you're tracking which user IDs have been processed:

const processed = [];

function process(id) {
    if (!processed.includes(id)) {
        processed.push(id);
        // do the work
    }
}

That includes check is O(n). Every call scans the whole array. For small lists it doesn't matter. For large ones it starts to hurt.

With a Set:

const processed = new Set();

function process(id) {
    if (!processed.has(id)) {
        processed.add(id);
        // do the work
    }
}

.has() on a Set is O(1). The Set also handles the uniqueness guarantee automatically.

Removing duplicates from an array is another place Sets show up in practice:

const nums = [1, 2, 2, 3, 4, 3, 5];
const unique = [...new Set(nums)];

console.log(unique); // [1, 2, 3, 4, 5]

One line. No filter, no reduce, no external library.

The tradeoff: Sets don't have index access. You can't do set[0]. If you need to access items by position, you still need an array.

Feature Array Set
Duplicates Allowed Not allowed
Index access Yes (arr[0]) No
.has() / .includes() O(n) O(1)
Maintaining order Insertion order Insertion order
Iteration Yes Yes

When to Use Map

  • Your keys aren't strings. Numbers, objects, functions as keys all work fine.

  • You're building a lookup table and need reliable size or iteration.

  • You're concerned about prototype pollution on a plain object.

  • You need to delete entries frequently. map.delete(key) is cleaner than delete obj.key.

A practical example: caching computed results by object reference.

const cache = new Map();

function getLabel(user) {
    if (cache.has(user)) {
        return cache.get(user);
    }
    const label = `\({user.firstName} \){user.lastName}`;
    cache.set(user, label);
    return label;
}

You can't do that with a plain object. The user object would get coerced to "[object Object]" as a key, and every user would collide.


When to Use Set

  • You need a unique list of anything.

  • You're doing frequent membership checks on a large collection.

  • You want to deduplicate an array quickly.

  • You're tracking "seen" or "visited" states.

Say you're building a tag input where users can add tags to a post, and you want to make sure the same tag can't be added twice:

const tags = new Set();

tags.add('javascript');
tags.add('webdev');
tags.add('javascript'); // already in there, silently ignored

console.log([...tags]); // ['javascript', 'webdev']
console.log(tags.size); // 2

No manual duplicate check needed. The Set just handles it.


WeakMap and WeakSet

JavaScript also has WeakMap and WeakSet. These are similar to Map and Set, but they hold weak references to their keys (WeakMap) or values (WeakSet). That means if the only reference to an object is inside a WeakMap, the garbage collector can still clean it up.

They're useful for attaching private metadata to objects without preventing garbage collection. But they're not iterable and don't have a .size.


Conclusion

Map and Set aren't exotic features. They're practical tools with real performance and correctness benefits over objects and arrays in specific situations. The key is knowing which situation you're in.

If your keys are dynamic or non-string, reach for Map. If your list needs to stay unique or you're checking membership often, reach for Set. Otherwise, objects and arrays are still perfectly fine.