Map Preserves Insertion Order, Accepts Any Key Type, and Has a Built-in Size
Map vs Object
Understand when to reach for Map over a plain object: key types, iteration order guarantees, size tracking, and performance trade-offs.
What you'll learn
- List the key differences between Map and a plain object
- Choose the correct structure based on key type and iteration requirements
- Measure Map size without counting keys manually
JavaScript gives you two built-in hash-table structures: Map and plain
objects ({}). Both offer O(1) average lookup, but they differ in ways that
matter for algorithmic code.
Key Types
Plain objects coerce all keys to strings (or Symbols). Map accepts any value
as a key — including objects, functions, and numbers — without coercion.
const obj = {};
obj[1] = "number key";
obj[true] = "boolean key";
console.log(Object.keys(obj)); // ["1", "true"] — coerced!
const map = new Map();
const keyObj = { id: 42 };
map.set(keyObj, "object key");
map.set(1, "number key");
map.set(true, "boolean key");
console.log(map.get(keyObj)); // "object key"
console.log(map.get(1)); // "number key"
console.log(map.size); // 3 Iteration Order
Map guarantees iteration in insertion order — always. Plain objects also
preserve insertion order for string keys in modern engines, except for
integer-like keys (e.g. “0”, “1”, “2”), which are sorted numerically first.
const obj = {};
obj["b"] = 2;
obj["a"] = 1;
obj["10"] = 10;
obj["2"] = 2;
console.log(Object.keys(obj)); // ["2", "10", "b", "a"] — integers first!
const map = new Map([["b", 2], ["a", 1], ["10", 10], ["2", 2]]);
console.log([...map.keys()]); // ["b", "a", "10", "2"] — insertion order This surprises many developers. When key ordering matters, always use Map.
Size
Map exposes .size in O(1). With a plain object you must call
Object.keys(obj).length, which is O(n).
Feature Comparison
| Feature | Map | Plain Object |
|---|---|---|
| Key types | Any value | String or Symbol |
| Insertion-order iter | Always | Except integer-like keys |
| Size | .size — O(1) | Object.keys().length — O(n) |
| Prototype pollution | None | Inherits from Object |
| JSON serialisable | No (needs custom logic) | Yes |
| Spread / destructure | Via Object.fromEntries | Native |
When to Pick Which
Use Map when:
- Keys are non-string values (DOM nodes, class instances, numbers).
- You need guaranteed iteration order regardless of key shape.
- You frequently read
.sizein a hot loop. - The data is purely algorithmic (frequency maps, adjacency maps).
Use a plain object when:
- You need JSON serialisation out of the box.
- You are modelling a static record with a fixed set of string keys.
- You need destructuring or spread syntax directly.
// Frequency map — Map is the natural fit
function charFrequency(str) {
const freq = new Map();
for (const ch of str) {
freq.set(ch, (freq.get(ch) ?? 0) + 1);
}
return freq;
}
console.log([...charFrequency("hello")]);
// [["h",1],["e",1],["l",2],["o",1]] Up Next
Explore Set — the hash-table cousin that stores unique values and supports
O(1) membership tests.