Why Should You Use Map Instead of Object?
TOC
Background
Object and Map are both data structures for storing key-value pairs, and some parts confused me, so I'm sorting it out here.
Differences in key order
1. When using integer keys
integer keys -> sorted in ascending order
1-1. Object
const obj = {};
obj[100] = 'a';
obj[2] = 'b';
obj[7] = 'c';
console.log(Object.keys(obj)); // ['2', '7', '100'] ← order flipped
1-2. Map
const map = new Map();
map.set(100, 'a');
map.set(2, 'b');
map.set(7, 'c');
console.log([...map.keys()]); // [100, 2, 7] ← kept as is
📌 Note
But why does it get sorted in ascending order?
When a key is an integer, it's given special treatment so it can be used 'like an array.'
- In js an array is ultimately an object too, so array access like arr[0] works internally as obj['0']
- Integer keys were deliberately designed to be sorted ascending so they behave like an array
const obj = {2: 'two', 1: 'one'};
console.log(Object.keys(obj)); // ['1', '2']
2. When using string keys
string keys (not integers) -> insertion order is preserved
2-1. Object
const obj2 = {};
obj2['banana'] = 1;
obj2['apple'] = 2;
obj2['cherry'] = 3;
console.log(Object.keys(obj2)); // ['banana', 'apple', 'cherry'] ← order preserved, apparently?
2-2. Map
const map2 = new Map();
map2.set('banana', 1);
map2.set('apple', 2);
map2.set('cherry', 3);
console.log([...map2.keys()]); // ['banana', 'apple', 'cherry']
3. When using a mix of numeric and string keys
3-1. Object
const obj3 = {};
obj3['3'] = 'three';
obj3['b'] = 'bee';
obj3['1'] = 'one';
obj3['a'] = 'ay';
console.log(Object.keys(obj3)); // ['1', '3', 'b', 'a'] ← integers sorted first
3-2. Map
const map3 = new Map();
map3.set('3', 'three');
map3.set('b', 'bee');
map3.set('1', 'one');
map3.set('a', 'ay');
console.log([...map3.keys()]); // ['3', 'b', '1', 'a'] ← insertion order intact
Differences in iteration
1. Object ( not iterable )
const obj = {};
obj[100] = 'a';
obj[2] = 'b';
obj[7] = 'c';
// enumerating the object's keys with for...in (not iteration)
for (const key in obj) {
console.log(key); // 2,7,100
}
// looping over key-value pairs
Object.entries(obj) // [['2','b'],['7','c'],['100','a']]
// looping over keys only
Object.keys(obj); // ['2','7','100']
// looping over values only
Object.values(obj); // ['a','b','c']
2. Map ( iterable )
const map = new Map();
map.set(100, 'a');
map.set(2, 'b');
map.set(7, 'c');
for (const [key, value] of map) {
console.log(key, value);
}
// 100 'a'
// 2 'b'
// 7 'c'
map.forEach((value,key,array)=>{
console.log(value,key,array);
});
// a 100 Map(3) {100 => 'a', 2 => 'b', 7 => 'c'}
// b 2 Map(3) {100 => 'a', 2 => 'b', 7 => 'c'}
// c 7 Map(3) {100 => 'a', 2 => 'b', 7 => 'c'}
📌 Note
When iterating an array with forEach, Array.prototype.forEach() has the shape arr.forEach((element,index,array)=>{}),
whereas Map is different — Map.prototype.forEach() has the shape map.forEach((value,key,map)=>{}).
Differences in speed
const N = 100,000;
const keys = Array.from({ length: N }, (_, i) => `key${i}`);
const obj = {};
keys.forEach((key, i) => {
obj[key] = i;
});
keys.forEach((key) => {
const val = obj[key];
});
const map = new Map();
keys.forEach((key, i) => {
map.set(key, i);
});
keys.forEach((key) => {
const val = map.get(key);
});
| Test | Throughput (ops/s) | Margin of error | Result |
| Map | 67 ops/s | ±5.72% | ✅ Fastest (baseline) |
| Object | 61 ops/s | ±6.4% | 🔻 about 10.25% slower |
| Item | Meaning |
| ops/s | operations per second; the bigger the number, the faster |
| ± percent | statistical margin of error |
| X% slower | how many % slower than the fastest test |
| Fastest | means this test recorded the fastest performance |
📌 Note
Running the same code several times on JSBench.me gives slightly different numbers each time, but the conclusion is the same: Map comes out a little faster.
References
https://tc39.es/ecma262/#sec-array.prototype.foreach
