Map.prototype.set()

The reason to choose a Map over an object: keys keep their type and their order. A number stays a number, an object works as a key, and nothing is coerced to a string.

Map methodES2015Live demo
Common call
map.set(key, value)
Returns
the same Map — chainable
Replaces
obj[key] = value, when keys are not strings
Watch out
object keys match by REFERENCE, not by contents
map.set(keykey — Any value at all — string, number, object, function, NaN, symbol. Matched by SameValueZero, so NaN works as a key and 0 and -0 are the same key.type: any · required, valuevalue — Any value. Setting an existing key overwrites it without changing its position in the insertion order.type: any · required)
→ Map

Demo

Live evaluation
Try:
Inputs
jsonstringJSON pairs, e.g. [["a",1]]
kstringkey to set
vnumbervalue to set
Output
new Map(JSON.parse('[["a",1]]')).set('b', 2)
Map(2) { 'a' => 1, 'b' => 2 }

The output is the Map itself, which is what set returns — hence the chainable style. Overwriting an existing key replaces its value but leaves it where it was in the order, so 'a' stays first. The numeric-key case shows the headline difference from a plain object: the keys 1 and 2 remain NUMBERS here, where an object would have converted them to the strings '1' and '2'.

Parameters

NameTypeRequiredDescription
keyanyyesAny value at all — string, number, object, function, NaN, symbol. Matched by SameValueZero, so NaN works as a key and 0 and -0 are the same key.
valueanyyesAny value. Setting an existing key overwrites it without changing its position in the insertion order.

Return value

Map — The SAME Map, so calls chain. It mutates in place — there is no non-mutating variant.

Common patterns

Chain several inserts
set returns the Map, so calls compose.
const m = new Map().set("a", 1).set("b", 2);
Build from pairs instead
The constructor takes any iterable of pairs.
const m = new Map([["a", 1], ["b", 2]]);
Use an object as a key
Associate data without touching the object.
const meta = new Map();
meta.set(domNode, {clicks: 0});

Examples

1. Chainable
new Map().set("a", 1).set("b", 2).size
Returns
2
2. Overwrites
new Map([["a", 1]]).set("a", 9).get("a")
Returns
9
3. Numbers stay numbers
new Map().set(1, "x").get(1)
Returns
'x'
4. String 1 is different
new Map().set(1, "x").get("1")
Returns
undefined
5. NaN works as a key
new Map().set(NaN, 1).get(NaN)
Returns
1
6. Objects by reference
new Map().set({}, 1).get({})
Returns
undefined

Pitfalls

1. Object keys match by reference, not contents
Two objects with identical properties are different keys. Building a Map keyed by freshly-created objects, or by objects parsed from JSON on each request, means every lookup misses — you must hold the same reference you stored.
A different object
const m = new Map();
m.set({id: 1}, "x");
m.get({id: 1})
undefined
Key by a primitive
m.set(1, "x");
m.get(1)
'x'
2. It mutates — there is no non-mutating set
Unlike the change-by-copy array methods, Map has no toSet or with. Sharing a Map across modules means any of them can change it, and React state holding a Map will not re-render unless you build a new one.
Same reference
const next = m.set("a", 1);
next === m
true
Copy first
const next = new Map(m).set("a", 1);
a new Map
3. A Map does not serialise to JSON
JSON.stringify sees no own enumerable properties and produces {} — the data vanishes silently. Convert with Object.fromEntries, or spread to pairs, before serialising.
Data lost
JSON.stringify(new Map([["a", 1]]))
'{}'
Convert first
JSON.stringify(Object.fromEntries(new Map([["a", 1]])))
'{"a":1}'
4. Keys are compared with SameValueZero
Almost === , with two differences: NaN is equal to itself, so it works as a key, and 0 and -0 are the SAME key. That last one can merge two entries you expected to be distinct.
Merged
new Map().set(0, "a").set(-0, "b").size
1
Distinguish deliberately
new Map().set("0", "a").set("-0", "b").size
2

When to use

Use it
  • Keys that are not strings — numbers, objects, DOM nodes, functions
  • Insertion order must be preserved for every key type
  • Frequent additions and deletions, where Map is faster than an object
  • Keys come from untrusted input, where __proto__ would be a hazard on an object
Reach for something else
  • String keys you will serialise to JSON → a plain object
  • A fixed, known set of fields → an object or a class
  • You need spread and destructuring → objects support them directly
  • Keys are objects you recreate each time → they will never match

Notes

Complexity
O(1) average
Return
The same Map, mutated
CPython impl
V8: Builtins-map / OrderedHashMap
Memory
Grows with the number of entries; keys are held strongly, so a Map can keep objects alive
Thread-safe
Single-threaded; mutating during iteration affects what the iterator yields

FAQ

Map when keys are not strings, when insertion order matters for numeric-looking keys, or when keys come from untrusted input. An object when the data is a fixed record you will serialise, spread or destructure — Map supports none of those directly.

new Map([[1, "a"], ["b", 2]]);   // key types kept
({1: "a", b: 2});                // keys become strings, reordered

History

ES2015
Map added with guaranteed insertion order and arbitrary key types.
ES2024
Map.groupBy added as a static, complementing Object.groupBy.