DocsHub
ES6+

Map and Set

Learn how to use Map and Set — two powerful ES6 data structures for storing unique values and key-value pairs.

Map and Set

JavaScript objects and arrays handle most data storage needs. But they have limitations in specific situations — objects only allow string keys, arrays allow duplicates. ES6 introduced two new data structures that solve these problems cleanly.

  • Map — like an object, but keys can be any type
  • Set — like an array, but every value must be unique

Map

A Map is a collection of key-value pairs where the keys can be any type — strings, numbers, objects, functions, even other Maps.

const map = new Map();

// Set values
map.set("name", "Ali");
map.set(1, "number key");
map.set(true, "boolean key");

const objKey = { id: 1 };
map.set(objKey, "object key");

// Get values
console.log(map.get("name"));   // Ali
console.log(map.get(1));        // number key
console.log(map.get(true));     // boolean key
console.log(map.get(objKey));   // object key

Creating a Map

Three ways to create a Map:

// Empty Map
const map1 = new Map();

// From an array of [key, value] pairs
const map2 = new Map([
  ["name", "Ali"],
  ["age", 22],
  ["city", "Lahore"]
]);

// From Object.entries()
const user = { name: "Ali", age: 22 };
const map3 = new Map(Object.entries(user));

console.log(map2.get("name")); // Ali
console.log(map3.get("age"));  // 22

Map Methods

const scores = new Map([
  ["Ali", 88],
  ["Sara", 92],
  ["Zara", 76]
]);

// set — add or update
scores.set("Omar", 85);
scores.set("Ali", 95); // updates existing key

// get — retrieve a value
console.log(scores.get("Sara")); // 92
console.log(scores.get("Hassan")); // undefined — not found

// has — check if key exists
console.log(scores.has("Zara")); // true
console.log(scores.has("Hassan")); // false

// delete — remove a key
scores.delete("Zara");
console.log(scores.has("Zara")); // false

// size — number of entries
console.log(scores.size); // 3

// clear — remove all entries
// scores.clear();

Iterating Over a Map

Maps are iterable. They maintain insertion order — unlike plain objects which may not.

const scores = new Map([
  ["Ali", 88],
  ["Sara", 92],
  ["Zara", 76]
]);

// for...of — gives [key, value] pairs
for (const [name, score] of scores) {
  console.log(`${name}: ${score}`);
}
// Ali: 88
// Sara: 92
// Zara: 76

// Keys only
for (const name of scores.keys()) {
  console.log(name); // Ali, Sara, Zara
}

// Values only
for (const score of scores.values()) {
  console.log(score); // 88, 92, 76
}

// Entries — same as for...of
for (const [key, value] of scores.entries()) {
  console.log(key, value);
}

// forEach
scores.forEach((score, name) => {
  console.log(`${name}: ${score}`);
});

Converting Between Map and Object

// Map to Object
const map = new Map([["name", "Ali"], ["age", 22]]);
const obj = Object.fromEntries(map);
console.log(obj); // { name: "Ali", age: 22 }

// Object to Map
const user = { name: "Ali", age: 22 };
const userMap = new Map(Object.entries(user));
console.log(userMap.get("name")); // Ali

Map vs Object — When to Use Which

// Object — simple key-value with string keys
const config = {
  theme: "dark",
  language: "en"
};

// Map — non-string keys, frequent additions/deletions, order matters
const elementData = new Map();
const btn = document.querySelector("button");
elementData.set(btn, { clicks: 0, lastClicked: null });
ObjectMap
Key typesStrings and Symbols onlyAny type
Key orderNot guaranteed for all casesAlways insertion order
SizeManual — Object.keys().length.size property
Iterationfor...in, Object.keys()for...of directly
PerformanceGood for small static dataBetter for frequent changes
Best forConfiguration, data recordsDynamic key-value storage

Real Use — Caching

// Cache expensive function results
function createCache(fn) {
  const cache = new Map();

  return function(...args) {
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      console.log("Cache hit");
      return cache.get(key);
    }

    console.log("Computing...");
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

function expensiveCalculation(n) {
  // Simulate slow computation
  return n * n * n;
}

const cachedCalc = createCache(expensiveCalculation);

console.log(cachedCalc(5)); // Computing... 125
console.log(cachedCalc(5)); // Cache hit — 125
console.log(cachedCalc(6)); // Computing... 216
console.log(cachedCalc(6)); // Cache hit — 216

Real Use — Counting Occurrences

const words = ["apple", "banana", "apple", "cherry", "banana", "apple"];

const frequency = new Map();

for (const word of words) {
  frequency.set(word, (frequency.get(word) ?? 0) + 1);
}

for (const [word, count] of frequency) {
  console.log(`${word}: ${count}`);
}
// apple: 3
// banana: 2
// cherry: 1

// Sort by frequency
const sorted = [...frequency.entries()]
  .sort((a, b) => b[1] - a[1]);

console.log(sorted);
// [["apple", 3], ["banana", 2], ["cherry", 1]]

Set

A Set is a collection of unique values. Adding the same value twice has no effect — duplicates are simply ignored.

const set = new Set();

set.add(1);
set.add(2);
set.add(3);
set.add(2); // duplicate — ignored
set.add(1); // duplicate — ignored

console.log(set); // Set { 1, 2, 3 }
console.log(set.size); // 3

Creating a Set

// Empty Set
const set1 = new Set();

// From an array — duplicates removed automatically
const set2 = new Set([1, 2, 3, 2, 1, 4]);
console.log(set2); // Set { 1, 2, 3, 4 }

// From a string — unique characters
const chars = new Set("javascript");
console.log(chars); // Set { "j", "a", "v", "s", "c", "r", "i", "p", "t" }

Set Methods

const fruits = new Set(["apple", "mango", "banana"]);

// add
fruits.add("grape");
fruits.add("apple"); // ignored — already exists

// has — check if value exists
console.log(fruits.has("mango")); // true
console.log(fruits.has("orange")); // false

// delete
fruits.delete("mango");
console.log(fruits.has("mango")); // false

// size
console.log(fruits.size); // 3

// clear — remove all
// fruits.clear();

Iterating Over a Set

Sets are iterable and maintain insertion order.

const fruits = new Set(["apple", "mango", "banana", "grape"]);

// for...of
for (const fruit of fruits) {
  console.log(fruit);
}
// apple, mango, banana, grape

// forEach
fruits.forEach(fruit => console.log(fruit));

// Spread to array
const arr = [...fruits];
console.log(arr); // ["apple", "mango", "banana", "grape"]

// Array.from
const arr2 = Array.from(fruits);

The Most Common Use — Removing Duplicates

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

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

One line. The cleanest way to deduplicate an array in JavaScript.

const tags = ["js", "css", "js", "html", "css", "react", "js"];
const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ["js", "css", "html", "react"]

Set Operations — Union, Intersection, Difference

Sets make set theory operations clean:

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

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

// Intersection — values in both sets
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]

// Symmetric difference — values in either but not both
const symmetricDiff = new Set([
  ...[...setA].filter(x => !setB.has(x)),
  ...[...setB].filter(x => !setA.has(x))
]);
console.log([...symmetricDiff]); // [1, 2, 3, 6, 7, 8]

Real Use — Tracking Unique Visitors

const visitedPages = new Set();

function trackVisit(page) {
  const isNew = !visitedPages.has(page);
  visitedPages.add(page);

  if (isNew) {
    console.log(`New page visited: ${page}`);
  } else {
    console.log(`Revisit: ${page}`);
  }
}

trackVisit("/home");      // New page visited: /home
trackVisit("/about");     // New page visited: /about
trackVisit("/home");      // Revisit: /home
trackVisit("/contact");   // New page visited: /contact

console.log(`Total unique pages: ${visitedPages.size}`); // 3
console.log([...visitedPages]); // ["/home", "/about", "/contact"]

Real Use — Permission System

const PERMISSIONS = {
  admin: new Set(["read", "write", "delete", "manage-users", "settings"]),
  editor: new Set(["read", "write"]),
  viewer: new Set(["read"])
};

function hasPermission(role, permission) {
  return PERMISSIONS[role]?.has(permission) ?? false;
}

function canAccess(role, requiredPermissions) {
  return requiredPermissions.every(p => hasPermission(role, p));
}

console.log(hasPermission("admin", "delete"));          // true
console.log(hasPermission("editor", "delete"));         // false
console.log(hasPermission("viewer", "read"));           // true

console.log(canAccess("admin", ["read", "write"]));     // true
console.log(canAccess("editor", ["read", "delete"]));   // false

Map and Set Together — A Real Example

// Track which users have seen which announcements
const announcements = new Map([
  ["ann-001", "New feature released!"],
  ["ann-002", "Scheduled maintenance tonight"],
  ["ann-003", "Welcome to the new dashboard"]
]);

const userSeenAnnouncements = new Map();

function markSeen(userId, announcementId) {
  if (!userSeenAnnouncements.has(userId)) {
    userSeenAnnouncements.set(userId, new Set());
  }
  userSeenAnnouncements.get(userId).add(announcementId);
}

function getUnseenAnnouncements(userId) {
  const seen = userSeenAnnouncements.get(userId) ?? new Set();
  return [...announcements.entries()]
    .filter(([id]) => !seen.has(id))
    .map(([id, text]) => ({ id, text }));
}

markSeen("user-1", "ann-001");
markSeen("user-1", "ann-003");

console.log(getUnseenAnnouncements("user-1"));
// [{ id: "ann-002", text: "Scheduled maintenance tonight" }]

console.log(getUnseenAnnouncements("user-2"));
// All three — user-2 has seen nothing

Map stores the announcements. A Set inside the Map tracks which ones each user has seen. Clean, efficient, and no duplicates possible.


Summary

Map:

  • Key-value pairs where keys can be any type
  • Maintains insertion order
  • Use set(), get(), has(), delete(), size
  • Iterable directly with for...of
  • Better than objects for dynamic keys, non-string keys, and frequent changes

Set:

  • Collection of unique values only — duplicates ignored
  • Maintains insertion order
  • Use add(), has(), delete(), size
  • Iterable directly with for...of
  • Best use — removing duplicates from arrays with [...new Set(array)]
  • Clean set operations — union, intersection, difference

When to use:

  • Map over object — non-string keys, dynamic additions, order matters
  • Set over array — uniqueness required, fast membership checks with .has()

On this page