JavaScript Map and Set: When to Use Them Over Objects

By Engr. Henry Akpan

Better Collections Than Plain Objects and Arrays

For years JavaScript developers used plain objects as dictionaries and arrays as lists of unique values. Both work, but both have traps: object keys are always turned into strings, objects come with inherited properties, and checking whether an array contains a value means scanning it from start to finish. ES2015 added two purpose-built collections, Map and Set, plus their "weak" versions. This JavaScript Map and Set tutorial explains how each works, when to choose them over objects and arrays, how to iterate and convert them, set operations such as union and intersection, practical patterns like counting and grouping, and what WeakMap and WeakSet are for. Each code block runs on its own in Node.js, and the outputs are real.

Map: Key–Value Pairs with Any Type of Key

A Map stores key–value pairs. Unlike an object, its keys can be any value, including numbers, objects and functions, and it remembers insertion order.

const scores = new Map();
scores.set("Ada", 85).set("Musa", 72);   // set returns the map, so calls chain
scores.set(2024, "year key");            // a number key stays a number

console.log(scores.get("Ada"), scores.has("Musa"), scores.size);
console.log(scores.get(2024), scores.get("2024"));
scores.delete(2024);
console.log([...scores.keys()], [...scores.values()]);

Output:

85 true 3
year key undefined
[ 'Ada', 'Musa' ] [ 85, 72 ]

Notice that get(2024) and get("2024") are different keys in a Map. In a plain object they would be the same, because object keys are converted to strings.

A Map can be created from an array of pairs, and turned back into one:

const capitals = new Map([["Lagos", "Ikeja"], ["Kano", "Kano"], ["Rivers", "Port Harcourt"]]);
for (const [state, capital] of capitals) {
  console.log(`${state}: ${capital}`);
}
console.log(Object.fromEntries(capitals));

Output:

Lagos: Ikeja
Kano: Kano
Rivers: Port Harcourt
{ Lagos: 'Ikeja', Kano: 'Kano', Rivers: 'Port Harcourt' }

Object.fromEntries(map) and new Map(Object.entries(obj)) convert between the two, which is useful because JSON only understands plain objects.

Objects as Map Keys

Using an object as a key is where Map really shines. With a plain object as the "dictionary", every object key turns into the same string:

const ada = { name: "Ada" };
const musa = { name: "Musa" };

const plain = {};
plain[ada] = "front row";
plain[musa] = "back row";
console.log(Object.keys(plain), plain[ada]);

const seats = new Map();
seats.set(ada, "front row");
seats.set(musa, "back row");
console.log(seats.get(ada), seats.get(musa), seats.size);

Output:

[ '[object Object]' ] back row
front row back row 2

The plain object silently overwrote Ada's seat, because both keys became "[object Object]". The Map keeps them separate, comparing object keys by identity.

Practical Map Patterns: Counting and Grouping

const grades = ["A", "B", "A", "C", "B", "A", "F"];
const counts = new Map();
for (const g of grades) counts.set(g, (counts.get(g) ?? 0) + 1);
console.log(counts);

const courses = [
  { code: "MTH101", level: 100 }, { code: "PHY201", level: 200 },
  { code: "CSC101", level: 100 }, { code: "MTH201", level: 200 }
];
const byLevel = new Map();
for (const c of courses) {
  if (!byLevel.has(c.level)) byLevel.set(c.level, []);
  byLevel.get(c.level).push(c.code);
}
console.log(byLevel);

Output:

Map(4) { 'A' => 3, 'B' => 2, 'C' => 1, 'F' => 1 }
Map(2) { 100 => [ 'MTH101', 'CSC101' ], 200 => [ 'PHY201', 'MTH201' ] }

Newer JavaScript versions include Map.groupBy(items, fn), which does the grouping in one call.

Set: A Collection of Unique Values

A Set stores each value at most once. Adding a value that is already present does nothing, and has checks membership quickly, without scanning the whole collection as array.includes does.

const visitors = new Set();
visitors.add("ada@uni.edu").add("musa@uni.edu").add("ada@uni.edu");
console.log(visitors.size, visitors.has("musa@uni.edu"));

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

const letters = new Set("banana");
console.log([...letters].join(""));

Output:

2 true
[ 3, 1, 2 ]
ban

[...new Set(array)] is the standard one-line way to remove duplicates from an array while keeping the first-seen order.

Set Operations

Union, intersection and difference are easy to build with spread and filter:

const maths = new Set(["Ada", "Musa", "Chidi", "Fatima"]);
const physics = new Set(["Musa", "Fatima", "Tunde"]);

const union = new Set([...maths, ...physics]);
const both = new Set([...maths].filter(x => physics.has(x)));
const mathsOnly = new Set([...maths].filter(x => !physics.has(x)));

console.log([...union]);
console.log([...both]);
console.log([...mathsOnly]);

Output:

[ 'Ada', 'Musa', 'Chidi', 'Fatima', 'Tunde' ]
[ 'Musa', 'Fatima' ]
[ 'Ada', 'Chidi' ]

Recent JavaScript engines (including current Node.js versions) also provide these as built-in methods: a.union(b), a.intersection(b), a.difference(b) and a.isSubsetOf(b). The spread-and-filter versions work in every environment.

How Values Are Compared

Map keys and Set values are compared using "SameValueZero", which is almost the same as ===, except that NaN equals itself. Objects are compared by identity, not by contents:

const s = new Set([NaN, NaN, 0, -0]);
console.log(s.size);

const points = new Set();
points.add({ x: 1, y: 2 });
points.add({ x: 1, y: 2 });
console.log(points.size);

const keyed = new Set(["1,2", "1,2"]);
console.log(keyed.size);

Output:

2
2
1

Two objects with the same contents are still two different objects. When you need "value" keys such as coordinates, convert them to a string key like "1,2".

JavaScript Map and Set or Object and Array?

  • Use a Map when keys are not strings, when keys are added and removed often, when you need the size quickly, or when keys come from user input (a plain object key like "__proto__" can cause bugs).
  • Use a plain object for fixed records with known property names, and for data you will send as JSON.
  • Use a Set for uniqueness and fast membership tests, such as visited items, tags or blocked users.
  • Use an array when order and position matter, duplicates are allowed, or you need methods like map, sort and reduce (you can always spread a Set into an array first).

WeakMap and WeakSet

A WeakMap only accepts objects as keys and holds them "weakly": if nothing else in the program refers to a key object, it can be garbage-collected and its entry disappears automatically. This makes WeakMaps good for attaching extra data to objects you don't own, such as caching results per object or storing private data for class instances, without causing memory leaks. A WeakSet similarly holds objects weakly, for example to mark which objects have already been processed. Because entries can vanish at any time, weak collections cannot be iterated and have no size.

Summary

  • JavaScript Map and Set are built-in collections that remember insertion order and are directly iterable.
  • Map keys can be any type, including objects; use set, get, has, delete and size.
  • Set stores unique values; [...new Set(arr)] removes duplicates.
  • Union, intersection and difference are simple with spread and filter, or built-in methods in modern engines.
  • Objects are compared by identity; WeakMap and WeakSet hold object keys without preventing garbage collection.

Related Tutorials