Skip to content
JavaScript js collections 4 min read

Map

A Map is a built-in collection that stores key/value pairs where the keys can be any value — objects, functions, numbers, or strings — not just the string keys a plain object allows. It remembers the order in which entries were inserted, exposes a handy size property, and is directly iterable. When you need a dictionary-like structure with rich keys or frequent additions and removals, Map is almost always the better choice than an object.

Why use a Map instead of an object

Plain objects work fine as simple string-keyed dictionaries, but they carry baggage: keys are coerced to strings, they inherit properties from Object.prototype (so a key like toString can collide with built-ins), and there is no built-in size or clean iteration. Map was designed specifically for the “collection of keyed data” use case.

FeatureMapPlain object
Key typesAny value (objects, functions, NaN, etc.)Strings and symbols only
Sizemap.sizeObject.keys(obj).length
Insertion orderGuaranteedMostly, but integer-like keys reorder
IterationDirectly iterable (for...of)Needs Object.keys/entries
Default keysNone — starts emptyInherits from prototype
Performance on frequent add/deleteOptimizedNot optimized

Reach for a Map whenever keys are unknown ahead of time, are not strings, or the collection changes size often. Use a plain object for fixed, known-at-author-time string keys (like config records).

Creating a Map

Construct an empty Map, or seed it from an iterable of [key, value] pairs.

const empty = new Map();

const sizes = new Map([
  ["small", 8],
  ["medium", 12],
  ["large", 16],
]);

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

Output:

3

CRUD: set, get, has, delete

The core API is small and consistent. set() returns the map itself, so calls can be chained.

const cache = new Map();

// Create / update
cache.set("user:1", { name: "Ada" })
     .set("user:2", { name: "Linus" });

// Read
console.log(cache.get("user:1")); // { name: 'Ada' }
console.log(cache.get("missing")); // undefined

// Existence check
console.log(cache.has("user:2")); // true

// Delete
cache.delete("user:1");
console.log(cache.has("user:1")); // false

// Remove everything
cache.clear();
console.log(cache.size); // 0

Output:

{ name: 'Ada' }
undefined
true
false
0

Because keys are compared by reference (using the SameValueZero algorithm), object keys are distinct unless they are literally the same reference:

const a = {};
const b = {};
const m = new Map();
m.set(a, "first").set(b, "second");

console.log(m.size);   // 2 — a and b are different objects
console.log(m.get(a)); // 'first'
console.log(m.get({})); // undefined — a brand-new object

Output:

2
first
undefined

Iterating a Map

A Map is iterable, so for...of yields [key, value] pairs in insertion order. You can also use keys(), values(), entries(), or forEach().

const scores = new Map([
  ["Ada", 95],
  ["Linus", 88],
  ["Grace", 99],
]);

for (const [name, score] of scores) {
  console.log(`${name}: ${score}`);
}

// Just the keys, or just the values
console.log([...scores.keys()]);   // ['Ada', 'Linus', 'Grace']
console.log([...scores.values()]); // [95, 88, 99]

// forEach gives (value, key) — note the order!
scores.forEach((score, name) => console.log(`${name} scored ${score}`));

Output:

Ada: 95
Linus: 88
Grace: 99
[ 'Ada', 'Linus', 'Grace' ]
[ 95, 88, 99 ]
Ada scored 95
Linus scored 88
Grace scored 99

Watch the argument order: Map.prototype.forEach((value, key) => ...) passes the value first, mirroring Array.prototype.forEach.

Converting to and from arrays and objects

Spreading a Map (or calling Array.from) produces an array of pairs, which makes sorting, filtering, and mapping trivial.

const prices = new Map([
  ["apple", 3],
  ["banana", 1],
  ["cherry", 5],
]);

// Map -> array of entries
const entries = [...prices]; // [['apple', 3], ['banana', 1], ['cherry', 5]]

// Sort by price, then rebuild a Map
const sorted = new Map([...prices].sort((a, b) => a[1] - b[1]));
console.log([...sorted.keys()]); // ['banana', 'apple', 'cherry']

// Map -> plain object
const obj = Object.fromEntries(prices);
console.log(obj); // { apple: 3, banana: 1, cherry: 5 }

// Object -> Map
const back = new Map(Object.entries(obj));
console.log(back.get("banana")); // 1

Output:

[ 'banana', 'apple', 'cherry' ]
{ apple: 3, banana: 1, cherry: 5 }
1

Note that Object.fromEntries only keeps keys that are valid object keys — string or symbol keys survive, but object keys are coerced to strings, so convert to a plain object only when your keys are strings.

Best Practices

  • Prefer Map over a plain object whenever keys are dynamic, non-string, or the collection is frequently mutated.
  • Chain set() calls for concise initialization, since each call returns the map.
  • Use map.size instead of computing length from keys, and map.has(key) instead of comparing get(key) to undefined (a stored undefined value is indistinguishable otherwise).
  • Remember that object keys are matched by reference — keep a handle to the key object if you need to look it up later.
  • Seed a Map from Object.entries(obj) and export it back with Object.fromEntries(map) only when keys are strings.
  • Use the spread operator to leverage array methods (sort, filter, map) and then reconstruct the Map.
Last updated June 1, 2026
Was this helpful?