Skip to main content

Command Palette

Search for a command to run...

Map and Set in JavaScript

Updated
11 min read

Objects and arrays do most of the heavy lifting in JavaScript. But sometimes they're the wrong tool, and that's exactly when Map and Set shine.


The Problem With What We Already Have

Before diving into Map and Set, it helps to understand why they were added in the first place.

Objects are great for storing key-value data. Arrays are great for storing lists. But both have specific limitations that show up in real code.

The problem with objects as key-value stores:

const visits = {};

visits["home"] = 10;
visits["about"] = 5;

// What if the key is not a string?
const userObject = { name: "Priya" };
visits[userObject] = 3;

console.log(visits);
// { home: 10, about: 5, '[object Object]': 3 }
//                        ↑ the object became a string — data is lost

Object keys can only be strings (or symbols). Anything else gets silently converted with .toString(). And that [object Object] key is completely useless.

The problem with arrays when you need uniqueness:

const tags = ["javascript", "code", "javascript", "tutorial", "code"];

// Arrays allow duplicates — you have to remove them manually
const unique = [...new Set(tags)]; // workaround needed

Arrays allow duplicate values. If you want a list where every item appears only once, you have to write extra logic to enforce it.

Map solves the first problem. Set solves the second. That's the whole story.


What Map Is

A Map is a collection of key-value pairs, just like an object. The difference is that any value can be a key in a Map. Not just strings. Not just symbols. Numbers, booleans, objects, functions, arrays, anything at all.

const map = new Map();

// String key
map.set("name", "Priya");

// Number key
map.set(1, "one");

// Boolean key
map.set(true, "yes");

// Object as key
const userKey = { id: 42 };
map.set(userKey, "Priya's data");

console.log(map.get("name"));    // "Priya"
console.log(map.get(1));         // "one"
console.log(map.get(true));      // "yes"
console.log(map.get(userKey));   // "Priya's data"

An object used as a key in a Map stays as an object. It doesn't get converted to "[object Object]". The Map remembers the exact reference.

Working With Map

Map comes with a clean set of methods:

const scores = new Map();

// Adding entries
scores.set("Priya", 95);
scores.set("Arjun", 88);
scores.set("Zara", 92);

// Reading
console.log(scores.get("Priya")); // 95
console.log(scores.has("Arjun")); // true
console.log(scores.has("Rahul")); // false

// Size
console.log(scores.size); // 3  ← not .length, it's .size

// Removing
scores.delete("Arjun");
console.log(scores.size); // 2

// Looping
scores.forEach((value, key) => {
  console.log(`\({key}: \){value}`);
});
// Priya: 95
// Zara: 92

One small thing to remember, it's .size, not .length. That's different from arrays.

Looping a Map Properly

Map is built to be iterated. You can loop over keys, values, or both:

const capitals = new Map([
  ["India", "New Delhi"],
  ["Japan", "Tokyo"],
  ["France", "Paris"]
]);

// Loop over key-value pairs
for (const [country, capital] of capitals) {
  console.log(`\({country} → \){capital}`);
}
// India → New Delhi
// Japan → Tokyo
// France → Paris

// Just keys
for (const country of capitals.keys()) {
  console.log(country);
}

// Just values
for (const capital of capitals.values()) {
  console.log(capital);
}

And one important thing, Map remembers insertion order. The items always come out in the exact order you put them in. Objects technically do this in modern JavaScript too, but only for string keys that aren't numbers, it's unreliable to depend on it.


What Set Is

A Set is a collection of values where every value must be unique. You can't add the same value twice. If you try, the Set simply ignores the duplicate, no errors, no warnings, it just doesn't add it.

const set = new Set();

set.add("apple");
set.add("banana");
set.add("apple"); // duplicate — ignored silently
set.add("cherry");

console.log(set.size); // 3 — not 4
console.log(set);      // Set(3) { 'apple', 'banana', 'cherry' }

That's the core property of Set. Every value appears exactly once.

Working With Set

const visited = new Set();

visited.add("home");
visited.add("about");
visited.add("contact");
visited.add("home"); // already there — ignored

console.log(visited.has("about"));   // true
console.log(visited.has("pricing")); // false
console.log(visited.size);           // 3

// Remove one
visited.delete("contact");
console.log(visited.size); // 2

// Loop
for (const page of visited) {
  console.log(page);
}
// home
// about

The Most Common Use: Removing Duplicates

The fastest way to deduplicate an array in JavaScript is to pass it through a Set and spread it back:

const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5];

const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4, 5]

Two characters of wrapping and two characters of spreading, and duplicates are gone. This works for strings too:

const tags = ["js", "code", "js", "tutorial", "code", "js"];
const uniqueTags = [...new Set(tags)];
// ["js", "code", "tutorial"]

Map vs Object: What's the Actual Difference?

At first glance, Map and Object look like they do the same job, both store key-value pairs. But they work quite differently in practice.

Key types:

// Object — keys become strings
const obj = {};
obj[1]    = "number key";
obj[true] = "boolean key";

console.log(Object.keys(obj)); // ["1", "true"] — converted to strings

// Map — keys stay as their original type
const map = new Map();
map.set(1, "number key");
map.set(true, "boolean key");

console.log([...map.keys()]); // [1, true] — original types preserved

Size:

const obj = { a: 1, b: 2, c: 3 };
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);

// Object — no built-in size
Object.keys(obj).length; // 3 — have to calculate it

// Map — has it built in
map.size; // 3

Inherited properties — a hidden danger of objects:

const cache = {};

cache["toString"] = "my value";

// But "toString" already exists on Object.prototype
// This overrides a built-in method — could cause unexpected bugs

const safeCache = new Map();
safeCache.set("toString", "my value"); // completely safe — no collision

Every plain object inherits properties from Object.prototype, things like toString, hasOwnProperty, constructor. If you use an object as a general-purpose dictionary, you risk accidentally colliding with these inherited properties. Map has no such problem because it doesn't inherit from Object.prototype in the same way.

Iteration:

const obj = { a: 1, b: 2 };
const map = new Map([["a", 1], ["b", 2]]);

// Object — needs conversion first
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

// Map — directly iterable
for (const [key, value] of map) {
  console.log(key, value);
}

When to use Map over Object:

  • Your keys aren't strings (numbers, objects, booleans)

  • You need to know the size quickly

  • You loop over the contents frequently

  • You're using it purely as a data store, not to describe a thing with methods

When to stick with Object:

  • You're modeling a real entity (a User, a Product, an Order) with known properties

  • You need methods attached, user.greet(), order.total()

  • You're passing data to JSON, JSON.stringify works naturally with objects, not Maps


Set vs Array: What's the Actual Difference?

Arrays and Sets both store lists of items. But they have fundamentally different guarantees.

Uniqueness:

// Array — allows duplicates
const arr = [1, 2, 2, 3, 3];
console.log(arr.length); // 5

// Set — allows only unique values
const set = new Set([1, 2, 2, 3, 3]);
console.log(set.size); // 3

Checking if a value exists:

const arr = ["apple", "banana", "cherry", "date", "elderberry"];
const set = new Set(["apple", "banana", "cherry", "date", "elderberry"]);

// Array — has to scan through every element
arr.includes("date"); // true — but slower for large lists

// Set — instant lookup, doesn't matter how big the Set is
set.has("date"); // true — always fast

This speed difference doesn't matter for 5 items. It matters a lot for 50,000 items.

Accessing by index:

const arr = ["a", "b", "c"];
arr[1]; // "b" — direct access by position

const set = new Set(["a", "b", "c"]);
// set[1] — doesn't work. Sets have no index.

You can't access Set elements by position. If you need that, you want an array.

When to use Set over Array:

  • You need every value to be unique, membership lists, tags, visited pages, selected IDs

  • You frequently check if something exists (.has() is faster than .includes())

  • You want to deduplicate existing array data

When to stick with Array:

  • Order and position matter, you need to access items by index

  • You need .map(), .filter(), .reduce(), those only exist on arrays

  • Duplicates are meaningful, a shopping cart can have the same item twice


Real-World Use Cases

Use Map: Tracking Visit Counts

const pageCounts = new Map();

function trackVisit(page) {
  const current = pageCounts.get(page) || 0;
  pageCounts.set(page, current + 1);
}

trackVisit("home");
trackVisit("about");
trackVisit("home");
trackVisit("home");

console.log(pageCounts.get("home"));  // 3
console.log(pageCounts.get("about")); // 1

Using an object here would work too, but if the page names ever include something like "constructor" or "toString", you'd have a silent bug. Map is safer.

Use Set: Managing Selected Items

const selectedIds = new Set();

function toggleSelection(id) {
  if (selectedIds.has(id)) {
    selectedIds.delete(id);
    console.log(`Deselected: ${id}`);
  } else {
    selectedIds.add(id);
    console.log(`Selected: ${id}`);
  }
}

toggleSelection(101); // Selected: 101
toggleSelection(102); // Selected: 102
toggleSelection(101); // Deselected: 101

console.log([...selectedIds]); // [102]

No risk of selecting the same item twice. The Set handles that automatically.

Use Set: Finding Common Items Between Two Lists

const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([3, 4, 5, 6, 7]);

// Intersection — items that exist in both
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log([...intersection]); // [3, 4, 5]

// Difference — items in A but not in B
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log([...difference]); // [1, 2]

Use Map: Caching Expensive Results

const cache = new Map();

function expensiveCalculation(n) {
  if (cache.has(n)) {
    return cache.get(n); // return cached result immediately
  }

  const result = n * n * n; // imagine this takes a long time
  cache.set(n, result);
  return result;
}

expensiveCalculation(10); // calculates and caches
expensiveCalculation(10); // returns from cache instantly
expensiveCalculation(20); // calculates and caches

Key Takeaways

  • Map is like an object, but any type of value can be a key, not just strings.

  • Set is like an array, but every value must be unique, duplicates are automatically ignored.

  • Use Map instead of an object when keys aren't strings, when you need .size, or when you loop over the data frequently.

  • Use Set when you need uniqueness, or when you frequently check whether something exists.

  • Object is still the right choice when you're modeling an entity with known properties and methods.

  • Array is still the right choice when order matters, you need index access, or you need .map()/.filter()/.reduce().

  • The fastest way to deduplicate an array: [...new Set(array)].

  • Set.has() is faster than Array.includes() for large collections.


Quick Reference

// ── MAP ──────────────────────────────────────────────
const map = new Map();
map.set(key, value);   // add or update
map.get(key);          // retrieve
map.has(key);          // true/false
map.delete(key);       // remove
map.size;              // number of entries
map.clear();           // remove all

// Loop
for (const [key, value] of map) { ... }
map.forEach((value, key) => { ... });

// Create from array of pairs
const map = new Map([["a", 1], ["b", 2]]);


// ── SET ──────────────────────────────────────────────
const set = new Set();
set.add(value);        // add (ignored if duplicate)
set.has(value);        // true/false
set.delete(value);     // remove
set.size;              // number of entries
set.clear();           // remove all

// Loop
for (const value of set) { ... }
set.forEach(value => { ... });

// Create from array (removes duplicates)
const set = new Set([1, 2, 2, 3]); // Set { 1, 2, 3 }

// Convert back to array
[...set]
Array.from(set)

// Deduplicate an array
const unique = [...new Set(array)];

Objects and arrays are the tools you reach for first. Map and Set are the tools you're glad exist when objects and arrays aren't quite right.