JavaScript maps vs objects

map: a key-value object that keeps the key as it is

Last post I summarized the differences in the basic usage of objects vs maps as a key-value item. This time there will be some more interesting issues and some fun / annoying parts of them.

The order of elements

Maps keep the element placed inside on order. You can’t say the same thing about objects,  as described in the post about the order of elements in a JavaScript object, in objects. On objects, as long as you know what you are doing,  you are pretty sure that they are where you placed them, but you still can not control or count on it. And the example given there was:

let o = {
   'b':1,
   2:2,
   5:3,
   [Symbol('foo')]:4,
   'a':5,
   3:6,
   1:7
};

// o == {
//   1: 7, 
//   2: 2, 
//   3: 6, 
//   5: 3, 
//   b: 1, 
//   a: 5, 
//   Symbol(foo): 4
// }

Different types of keys

A. Numeric keys:

As described in the post about JavaScript object keys, JavaScript object converts all keys into strings (except symbols). That’s why if you put two different keys, one a number and the other as a string. both with the same value.  The ladder will override the first:

let o  = {
    2 : 'a',
   '2': 'b'
};
// o == {'2':'b'}

Maps keep keys like that are, so on the same example you will get:

let m = new Map();
m.set(2,'a')
 .set('2','b');
m.size == 2;
m.get(2) == "a";
m.get('2') == "b";

B. Object as keys:

Since maps keep the keys as they are. you can use functions and objects as keys:

let o1 = {o:1}; 
let o2 = {o:2}; 
m.set(o1,'o1'); 
m.set(o2,'o2'); 
m.get(o1) == 'o1'; 
m.get(o2) == 'o2'; 
o[o1] = 'o1'; 
o[o2] = 'o2; 
o[o1] != 'o1'; 
o[o1] == 'o2'; 
o[o2] == 'o2'; 
o[{}] == 'o2'; 
o[new Object] == 'o2';

C. Function as keys

let f = function(){ return "f1"}
let o = {};
o[f] = "ok";

o[f] == "ok";
o['function(){return "f1"}'] == "ok";
f = function(){ return "f2"}
o[f] == undefined;

let m = new Map();
m.set(f,"ok");

m.get(f) == "ok";
m.get('function(){return "f1"}') == undefined
f = function(){}
m.get(f) == undefined;

D. Arrays as keys

After you got the principle, this one will be straightforward:

let arr = [];

o[arr] = 'arr';
o[[]] == 'arr';
o[""] == 'arr';

m.set(arr,'arr');
m.get([]) == undefined;
m.get("") == undefined;

arr.push('value');

o[arr] == undefined;
m.get(arr) == 'arr';

Iteration and symbols

I covered the different syntax and options on the introduction to JavaScript maps. I’ll just remind that object deals with symbols as metadata, so you will not get them on the same iteration as the other values:

let o = {1:'a','b':'b',[Symbol('c'):'c']};

Object.keys(o) => ["1","b"];
Object.getOwnPropertyNames(o) => ["1","b"]
Object.getOwnPropertySymbols(o) => [Symbol(c)]
Object.getOwnPropertyDescriptors(o) => [
   1:{...},
   'b':{...},
   Symbol(c):{...}
];

m = new Map()
 .set(1,'a')
 .set('b','b')
 .set(Symbol('c'),'c');

m.keys() => MapIterator {1,"b",Symbol(c)}
m.forEach(function(value,key){
           ...
           // 'a',1
           // 'b', 'b'
           // 'c', Symbol(c)
       }) =>

Conclusion: don’t eat a tomato while riding the bike!