DEV Community

Cover image for Map and Set in JavaScript
Pratham
Pratham

Posted on

Map and Set in JavaScript

Two data structures that solve problems objects and arrays can't — and when you should reach for them.


For a while, I thought JavaScript only had two ways to store data: objects (key-value pairs) and arrays (ordered lists). They handle most situations, so I never questioned whether there was something better.

Then I ran into two problems that made me realize their limitations:

Problem 1: I needed an object where the keys were other objects, not just strings. Regular objects can't do that — every key gets converted to a string.

Problem 2: I needed an array with no duplicates. I kept adding values and then manually checking "wait, is this already in there?" before each insertion.

Both problems have clean solutions: Map and Set. They were introduced in ES6 (2015), and once you understand what they do, you'll wonder why you didn't learn them sooner.

Let me walk you through both.


What Is a Map?

A Map is a collection of key-value pairs — similar to an object — but with some important upgrades.

The biggest difference: Map keys can be anything. Objects, arrays, numbers, functions — literally any value can be a key. With regular objects, keys are always converted to strings.

Creating and Using a Map

const userRoles = new Map();

// Setting key-value pairs
userRoles.set("Pratham", "admin");
userRoles.set("Arjun", "editor");
userRoles.set("Priya", "viewer");

// Getting values
console.log(userRoles.get("Pratham")); // "admin"
console.log(userRoles.get("Arjun")); // "editor"

// Checking if a key exists
console.log(userRoles.has("Priya")); // true
console.log(userRoles.has("Rahul")); // false

// Size
console.log(userRoles.size); // 3

// Deleting
userRoles.delete("Arjun");
console.log(userRoles.size); // 2
Enter fullscreen mode Exit fullscreen mode

You Can Also Initialize with Values

const config = new Map([
  ["theme", "dark"],
  ["language", "en"],
  ["fontSize", 16],
]);

console.log(config.get("theme")); // "dark"
Enter fullscreen mode Exit fullscreen mode

Visual: Map Key-Value Storage

┌──────────────────────────────────────┐
│              Map                     │
├──────────────┬───────────────────────┤
│     KEY      │        VALUE          │
├──────────────┼───────────────────────┤
│  "Pratham"   │       "admin"         │
│  "Arjun"     │       "editor"        │
│  "Priya"     │       "viewer"        │
├──────────────┴───────────────────────┤
│  .get(key) → value                   │
│  .set(key, value) → adds/updates     │
│  .has(key) → true/false              │
│  .delete(key) → removes pair         │
│  .size → number of pairs             │
└──────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The Superpower: Any Value as a Key

This is where Map pulls ahead of regular objects:

const scores = new Map();

// Object as key — impossible with regular objects
const student1 = { name: "Pratham" };
const student2 = { name: "Arjun" };

scores.set(student1, 95);
scores.set(student2, 88);

console.log(scores.get(student1)); // 95
console.log(scores.get(student2)); // 88
Enter fullscreen mode Exit fullscreen mode

With a regular object, both keys would become the string "[object Object]" and overwrite each other. Map keeps them as separate, distinct keys.

// Number as key
const cache = new Map();
cache.set(404, "Not Found");
cache.set(200, "OK");
console.log(cache.get(404)); // "Not Found"

// Function as key
const fn = () => "hello";
cache.set(fn, "greeting function");
console.log(cache.get(fn)); // "greeting function"
Enter fullscreen mode Exit fullscreen mode

Iterating Over a Map

Maps maintain insertion order and are directly iterable:

const fruits = new Map([
  ["apple", 3],
  ["banana", 5],
  ["mango", 2],
]);

// for...of with destructuring
for (const [fruit, count] of fruits) {
  console.log(`${fruit}: ${count}`);
}
// apple: 3
// banana: 5
// mango: 2

// Individual iterators
console.log([...fruits.keys()]); // ["apple", "banana", "mango"]
console.log([...fruits.values()]); // [3, 5, 2]
console.log([...fruits.entries()]); // [["apple", 3], ["banana", 5], ["mango", 2]]
Enter fullscreen mode Exit fullscreen mode

Map vs Object — When Each One Wins

This was my biggest question: "Why not just use a regular object?" Here's the honest comparison:

Feature Object Map
Key types Strings and Symbols only Any value (objects, numbers, etc.)
Key order Not guaranteed (mostly works) ✅ Guaranteed insertion order
Size Manual: Object.keys(obj).length Direct: map.size
Iteration Indirect (Object.keys/entries) Direct (for...of, .forEach)
Performance Slower for frequent add/delete ✅ Optimized for frequent changes
Default keys Has prototype keys (toString, etc.) ✅ No default keys — clean
JSON support ✅ Direct JSON.stringify() Needs manual conversion
Destructuring/spread { ...obj } Not directly

When to Use Object

  • JSON data from APIs
  • Configuration objects with known string keys
  • When you need destructuring or spread
  • Simple, static key-value structures

When to Use Map

  • Keys that aren't strings (objects, numbers, DOM elements)
  • Frequent additions and deletions
  • When insertion order matters
  • When you need .size instead of counting manually
  • Caching or lookup tables
// ✅ Object — perfect for API data
const user = { name: "Pratham", age: 22, city: "Delhi" };

// ✅ Map — perfect for tracking DOM elements
const clickCounts = new Map();
// clickCounts.set(buttonElement, 0);
// clickCounts.set(linkElement, 0);
Enter fullscreen mode Exit fullscreen mode

What Is a Set?

A Set is a collection of unique values. No duplicates allowed — ever. If you try to add a value that already exists, the Set simply ignores it.

Think of it like a guest list at a party. Each person can only appear once. You can try writing "Pratham" ten times — the list still only shows "Pratham" once.

Creating and Using a Set

const skills = new Set();

skills.add("JavaScript");
skills.add("React");
skills.add("Node.js");
skills.add("JavaScript"); // Duplicate — silently ignored

console.log(skills); // Set(3) {"JavaScript", "React", "Node.js"}
console.log(skills.size); // 3 — not 4!
Enter fullscreen mode Exit fullscreen mode

Set Methods

const colors = new Set(["red", "green", "blue"]);

// Check if a value exists
console.log(colors.has("red")); // true
console.log(colors.has("purple")); // false

// Delete a value
colors.delete("green");
console.log(colors); // Set(2) {"red", "blue"}

// Clear everything
colors.clear();
console.log(colors.size); // 0
Enter fullscreen mode Exit fullscreen mode

Visual: Set Uniqueness

Adding values to a Set:

  .add("JS")     → Set: { "JS" }
  .add("React")  → Set: { "JS", "React" }
  .add("Node")   → Set: { "JS", "React", "Node" }
  .add("JS")     → Set: { "JS", "React", "Node" }  ← IGNORED (duplicate)
  .add("React")  → Set: { "JS", "React", "Node" }  ← IGNORED (duplicate)
  .add("Vue")    → Set: { "JS", "React", "Node", "Vue" }

  Only unique values survive. Duplicates are silently rejected.
Enter fullscreen mode Exit fullscreen mode

The Killer Use Case: Removing Duplicates from an Array

This is the most common reason you'll reach for Set:

const numbers = [1, 3, 5, 3, 7, 1, 9, 5, 3];

// One line — duplicates gone
const unique = [...new Set(numbers)];
console.log(unique); // [1, 3, 5, 7, 9]
Enter fullscreen mode Exit fullscreen mode

Before Set, you'd need a loop with an includes() check or a filter with indexOf. Now it's one line.

// Works with strings too
const tags = ["js", "react", "js", "node", "react", "css"];
const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ["js", "react", "node", "css"]
Enter fullscreen mode Exit fullscreen mode

Iterating Over a Set

Sets maintain insertion order and are directly iterable:

const fruits = new Set(["Apple", "Mango", "Banana"]);

for (const fruit of fruits) {
  console.log(fruit);
}
// Apple
// Mango
// Banana

// Convert to array anytime
const fruitArray = [...fruits];
console.log(fruitArray); // ["Apple", "Mango", "Banana"]
Enter fullscreen mode Exit fullscreen mode

Set vs Array — When Each One Wins

Feature Array Set
Duplicates ✅ Allowed ❌ Automatically removed
Order ✅ Index-based ✅ Insertion order
Access by index arr[0] ❌ No index access
Check existence .includes() — O(n) .has() — O(1) ⚡
Size .length .size
Methods map, filter, reduce, etc. add, delete, has
Best for Ordered lists, transformation Unique values, fast lookups

The performance difference in .has() vs .includes() is worth highlighting. For small arrays, you won't notice. But if you're checking whether a value exists in a collection of 10,000 items, Set is dramatically faster because it uses a hash-based lookup (constant time) instead of scanning every element (linear time).

When to Use Array

  • You need index-based access
  • Order and position matter
  • You need map(), filter(), reduce()
  • Duplicates are acceptable or expected

When to Use Set

  • You need guaranteed uniqueness
  • You frequently check "does this value exist?"
  • You're removing duplicates from data
  • You don't need index-based access

Practical Use Cases

1. Tracking Unique Visitors

const visitors = new Set();

// As users visit
visitors.add("user_101");
visitors.add("user_202");
visitors.add("user_101"); // Returning visitor — not counted again

console.log(`Unique visitors: ${visitors.size}`); // 2
Enter fullscreen mode Exit fullscreen mode

2. Caching API Results with Map

const apiCache = new Map();

const fetchUser = async (userId) => {
  if (apiCache.has(userId)) {
    console.log("Cache hit!");
    return apiCache.get(userId);
  }

  console.log("Fetching from API...");
  // const response = await fetch(`/api/users/${userId}`);
  // const data = await response.json();
  const data = { id: userId, name: "Pratham" }; // simulated

  apiCache.set(userId, data);
  return data;
};
Enter fullscreen mode Exit fullscreen mode

3. Counting Word Frequency with Map

const text = "the cat sat on the mat the cat";
const words = text.split(" ");

const frequency = new Map();
for (const word of words) {
  frequency.set(word, (frequency.get(word) || 0) + 1);
}

console.log(frequency);
// Map(5) { "the" => 3, "cat" => 2, "sat" => 1, "on" => 1, "mat" => 1 }
Enter fullscreen mode Exit fullscreen mode

4. Finding Common Elements Between Arrays

const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];

const set2 = new Set(arr2);
const common = arr1.filter((item) => set2.has(item));
console.log(common); // [3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Using a Set for the lookup makes this O(n) instead of O(n²) — a real performance win for large arrays.

5. Map for Readable Enums / Lookups

const httpStatus = new Map([
  [200, "OK"],
  [201, "Created"],
  [400, "Bad Request"],
  [401, "Unauthorized"],
  [404, "Not Found"],
  [500, "Internal Server Error"],
]);

console.log(httpStatus.get(404)); // "Not Found"
console.log(httpStatus.get(200)); // "OK"
Enter fullscreen mode Exit fullscreen mode

Let's Practice: Hands-On Assignment

Part 1: Map Basics

const studentGrades = new Map();

studentGrades.set("Pratham", "A");
studentGrades.set("Arjun", "B+");
studentGrades.set("Priya", "A+");

console.log(studentGrades.get("Priya")); // "A+"
console.log(studentGrades.has("Rahul")); // false
console.log(studentGrades.size); // 3

for (const [name, grade] of studentGrades) {
  console.log(`${name}: ${grade}`);
}
Enter fullscreen mode Exit fullscreen mode

Part 2: Set — Remove Duplicates

const rawData = [10, 20, 30, 20, 40, 10, 50, 30, 60];

const uniqueData = [...new Set(rawData)];
console.log(uniqueData); // [10, 20, 30, 40, 50, 60]
Enter fullscreen mode Exit fullscreen mode

Part 3: Map with Non-String Keys

const config = new Map();

config.set(1, "First Priority");
config.set(true, "Feature Enabled");
config.set({ id: 1 }, "Object Key");

console.log(config.get(1)); // "First Priority"
console.log(config.get(true)); // "Feature Enabled"
console.log(config.size); // 3
Enter fullscreen mode Exit fullscreen mode

Part 4: Set Operations — Union, Intersection, Difference

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

// Union — all unique values from both
const union = new Set([...setA, ...setB]);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]

// Intersection — values in both
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]
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  1. Map is a key-value collection where keys can be any value — objects, numbers, functions, not just strings. Use it when regular objects fall short.
  2. Set is a collection of unique values. Duplicates are automatically rejected. The fastest way to deduplicate an array: [...new Set(arr)].
  3. Map vs Object: Use Map for non-string keys, frequent add/delete, guaranteed order, and .size. Use Object for JSON data and simple config.
  4. Set vs Array: Use Set for uniqueness and fast .has() lookups. Use Array for ordered data with index access and transformation methods.
  5. Both are iterable and maintain insertion order. They work seamlessly with for...of, spread, and destructuring.

Wrapping Up

Map and Set aren't replacements for objects and arrays — they're complements. Each has its strengths, and knowing when to reach for which one is a sign of a developer who thinks about the right tool for the job, not just the first tool that comes to mind.

I'm learning all of this through the ChaiCode Web Dev Cohort 2026 under Hitesh Chaudhary and Piyush Garg. Map and Set were one of those topics where I initially thought "do I really need these?" — and now I use them regularly, especially Set for deduplication and Map for caching. They're worth learning early.

Connect with me on LinkedIn or visit PrathamDEV.in to follow along. More articles coming as the journey continues.

Happy coding! 🚀


Written by Pratham Bhardwaj | Web Dev Cohort 2026, ChaiCode

Top comments (0)