A map is one of the most frequently used data structures in daily programming. It keeps key-value pairs that can easily be accessed by their keys. In Java, it is pretty obvious to use a HashMap for that. However, in JavaScript, it is quite convenient to just use a plain object for that purpose:
const map = {};
// insert key-value-pair
map['key1'] = 'value1';
map['key2'] = 'value2';
map['key3'] = 'value3';
// check if map contians key
if (map['key1']) {
console.log('Map contains key1');
}
// get value with specific key
console.log(map['key1']);
But there is a built-in data structure in JavaScript that was made for exactly that purpose: Map. Let me give you some reasons to prefer Map over plain objects.
Objects can only have symbols or strings. Maps can have any type of value as a key: objects, functions, or primitives.
const map = new Map();
const myFunction = () => console.log('I am a useful function.');
const myNumber = 666;
const myObject = {
name: 'plainObjectValue',
otherKey: 'otherValue'
};
map.set(myFunction, 'function as a key');
map.set(myNumber, 'number as a key');
map.set(myObject, 'object as a key');
console.log(map.get(myFunction)); // function as a key
console.log(map.get(myNumber)); // number as a key
console.log(map.get(myObject)); // object as a key
While a Map provides a size property, the size of a plain object has to be determined the hard way. Determining the size of a Map is possible in O(1) time, while it takes O(n) steps for a plain object.
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 1);
...
map.set('someKey100', 1);
console.log(map.size) // 100, Runtime: O(1)
const plainObjMap = {};
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 1;
...
plainObjMap['someKey100'] = 1;
console.log(Object.keys(plainObjMap).length) // 100, Runtime: O(n)
Maps are optimized for frequent additions and removals of entries.
Moreover, the number of entries of a Map can be retrieved in constant time, while the number of entries of a plain object must be counted, which takes O(n) time.
As an example using my Macbook Pro, this is the average duration of size determination of a map with 10 million entries:
Furthermore, it is not necessary to convert any key into a string, which can save a lot of time.
Objects must be iterated by getting the keys and iterating over them. On the other hand, a Map is iterable, which means it can be iterated directly.
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 2);
map.set('someKey3', 3);
for (let [key, value] of map) {
console.log(`${key} = ${value}`);
}
// someKey1 = 1
// someKey2 = 2
// someKey3 = 3
const plainObjMap = new Map();
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 2;
plainObjMap['someKey3'] = 3;
for (let key of Object.keys(plainObjMap)) {
const value = plainObjMap[key];
console.log(`${key} = ${value}`);
}
// someKey1 = 1
// someKey2 = 2
// someKey3 = 3
Before ECMAScript 2015, the keys of an object are not guaranteed to be in any specific order. Iterating over a Map guarantees that the keys appear in the order of insertion.
A plain object already contains some keys because of its prototype. There may be collisions between your keys and those already contained by the object. Maps do not contain any keys on creation.
Note: Since ECMAScript 2015, you can avoid an accidental key overriding by using Object.create(null)
to create your plain object map.
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 2);
map.set('toString', 3); // No problem for Map
const plainObjMap = new Map();
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 2;
plainObjMap['toString'] = 3; // Oops, native property
The time of using objects as a workaround to a missing implementation of hash maps is over. Objects are very useful, but they are not the first choice anymore when confronted with a typical hash map use case.
Thank you for reading!
Stop Using Objects as Hash Maps in JavaScript
#javascript #nodejs #react #programming