Object and Map are quite confusing when we handle data. In which situation do you choose Map instead of using Object?

The plain Object in javascript is a data structure holding the structured data. An Object using key-value pair for storing data.

For example, we got this object:


const names = { 1: 'Luong', 2: 'Bob', };

Map is a data structure that helps in storing the data in the form of pairs. The pair consists of a unique key and a value mapped to the key. It helps prevent duplicity.

const map = new Map([
[3,1],
[2,9]]);

The differences between Map and Object are as follows

  • In Object, the data-type of the key-field is restricted to integer, strings, and symbols. Whereas in Map, the key-field can be of any data-type (integer, an array, even an object!)
  • In the Map, the original order of elements is preserved. This is not true in case of objects.
  • The Map is an instance of an object but the vice-versa is not true.

The keys in the object have to be strings (sometimes we can use symbols). If we try to assign keys as numbers like that. It will convert to strings when the object’s keys are accessed.

const names = {
  1: 'Luong',
  2: 'Bob',
};
 
Object.keys(names); // => ['1', '2']

So, in case you want to use objects like keys, Map object this the best choice to resolve that issue.

In our daily work, sometimes we need to consider using Map to make our code better as follows

The map accepts any key type

Map accepts keys of any type: strings, numbers, boolean, symbols, … even object.

const booleansMap = new Map();
booleansMap.set(true, "Yes");
booleansMap.set(false, "No");
[...booleansMap.keys()]; // => [true, false]

When you using a non-primitive type likes object or array as a key, be careful of memory leaks.

Object as key

Let’s say you need to store some object-related data, without attaching this data on the object itself.

const foo = { name: 'foo' };
const bar = { name: 'bar' };
const normalMap = [
[foo, {data: 'Foo related data'}],
[bar, {data: 'Bar related data'}],
];

The downside of this approach is the O(n) complexity of accessing the value by key. You have to loop through the entire array to get the desired value:

function getByKey(normalMap, key) {
for (const [k, v] of normalMap) {
if (key===k) {
returnv;
}
}
return undefined;
}
getByKey(kindOfMap, foo); // => 'Foo related data'

To prevent memory leaks when we use objects as keys. Try the WeakMap to straightforward access value by the key, with O(1) complexity.

const foo = { name: 'foo' }; 
const bar = { name: 'bar' };
const mapOfObjects = new WeakMap();
mapOfObjects.set(foo, {data: 'Foo related data'}); mapOfObjects.set(bar, {data: 'Bar related data'}); mapOfObjects.get(foo); // => 'Foo related data'

The main difference between Map and WeakMap is the latter allowing garbage collection of keys (which are objects). This prevents memory leaks.

The map has no restriction over keys names

It is very dangerous if we use the key of an object to render. In a specific case, if I use the key “constructor” it accidentally caused a dangerous situation because we have overwritten property inherited from the prototype.
For example, if we have a form using the input of the user as a key for an object.

javascript table

It would be convenient to store the state of the custom fields into a plain object:

const userCustomFields = { 
'color': 'blue',
'size': 'medium',
'toString': 'A blue box'
};

But the user can choose a custom field name like toString (as in the example), constructor, etc. As presented above, such key names on the state object could potentially break the code that later uses this object.

Don’t take user input to create keys on your plain objects!

Because the map has no restrictions over the key names, the right solution is to bind the user interface state to a map.

 
const userCustomFields = = new Map({ 
'color': 'blue',
'size': 'medium',
'toString': 'A blue box'
});

There is no way to break the map, even using keys as toStringconstructor, etc.

The map is iterable

Object.entries(colorsHex) returns an array of key-value pairs extracted from the object.

Access of keys-values of a map is more comfortable because the map is iterable. Anywhere an iterable is accepted, like for() loop or spread operator, use the map directly.

colorsHexMap keys-values are iterated directly by for() loop:

const colorsHexMap = new Map(); 
colorsHexMap.set('white', '#FFFFFF');
colorsHexMap.set('black', '#000000');
for (const [color, hex] of colorsHexMap) {
console.log(color, hex);
}
// 'white' '#FFFFFF'
// 'black' '#000000'

colorsHexMap is iterable. You can use it anywhere an iterable is accepted: for() loops, spread operator [...map].

Moreover, map.keys() returns an iterator over keys and map.values() over values.

Map’s size

If we always feel tired when calculating the length of an object. Map can help you solve this issue simply.

const examsMap = new Map([ 
['John Smith', '10 points'],
['Jane Doe', '8 points'],
]);
examsMap.size; // => 2

It’s simple to determine the size of the map: examsMap.size.

Conclusion

Plain JavaScript objects do the job of holding structured data. But they have some limitations: 

  • Only strings or symbols can be used as keys
  • Own object properties might collide with property keys inherited from the prototype (e.g. toStringconstructor, etc).
  • Objects cannot be used as keys

These limitations are solved by Map or WeekMap. Moreover, maps provide benefits like being iterators and allowing easy size look-up.

Anyways, don’t consider maps as a replacement for plain objects, but rather a complement.


Note: I just said  “Map object”. That right, Map is an instant of object. Let’s try:

const map = new Map([
[3,1],
[2,9]
]);

//output:true
console.log(map instanceofObject);
//output:false
const obj = new Object();
console.log(obj instanceofMap);