Storing Key-Value Pairs With JavaScript Maps
Tags: javascript, webdev, beginners, tutorial
Published: 2020-01-14 03:40:47

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

**Many more articles at https://medium.com/@hohanga**

With ES2015, we have a new data structure to store key-value pairs, called Maps, which are dictionaries that we can use to store any object as keys and any object as values.

Before we had Maps, we had to use objects as dictionaries that only use strings as keys, but values could be anything.

Using Maps is simple, we can define Maps like in the following code:

```javascript
let messageMap = new Map();  
messageMap.set('hi', 'Hello');  
messageMap.set('greeting', 'How are you doing?');  
messageMap.set('bye', 'Bye');
```

Instead of using the `set` method to add our keys and values, we can also pass a nested array where each entry of the array has the key as the first element and the value as the second element.

For example, we can write:

```javascript
const arrayOfKeysAndValues = [  
  ['a', 1],  
  ['b', 2],  
  ['c', 3],  
  ['d', 3],  
  ['e', 4],  
  ['f', 5],  
  ['g', 6],  
  ['h', 7],  
  ['i', 8],  
  ['j', 9],  
  ['k', 10],  
  ['l', 11],  
  ['m', 12],  
  ['n', 13],  
  ['o', 14],  
]  
let numMap = new Map(arrayOfKeysAndValues);  
console.log(numMap)
```

The code above will create a Map with the first element of each array as the key and the second element of each array as the corresponding value. So, after running the `console.log` line, we get all the entries of the Map listed in the same order as the array.

We can get the number of key-value pairs defined in a Map by using the `size` property:

```javascript
messageMap.size // 3
```

To get the value by the key, we use the `get` function:

```javascript
messageMap.get('hi'); // 'Hello'  
messageMap.get('hello'); // undefined
```

It returns the value if the key exists and `undefined` if an entry with the key doesn’t exist.

To remove an entry given the key, we can use the `delete` function with the key passed in:

```javascript
messageMap.delete('hi');
```

If we call `set` with the same key passed in more than once, then the value in the later `set` call overwrites the earlier one. For example:

```javascript
let messageMap = new Map();  
messageMap.set('hi', 'Hello');  
messageMap.set('greeting', 'How are you doing?');  
messageMap.set('bye', 'Bye');  
messageMap.set('hi', 'Hi');
console.log(messageMap.get('hi'))
```

If we run `console.log(messageMap.get(‘hi’))`, we get `'Hi'` instead of `'Hello'`.

To clear all the entries of a Map object, we can call the `clear` function, like in the following code:

```javascript
let messageMap = new Map();  
messageMap.set('hi', 'Hello');  
messageMap.set('greeting', 'How are you doing?');  
messageMap.set('bye', 'Bye');  
messageMap.set('hi', 'Hi');  
messageMap.clear();  
console.log(messageMap)  
console.log(messageMap.size)
```

We see that `messageMap` should be empty when we log it and that its `size` should be 0.

The entries of a Map can be iterated over with a `for...of` loop. With the destructuring assignment of each entry, we can get the key and value of each entry with a `for...of` loop like in the following code:

```javascript
let messageMap = new Map();  
messageMap.set('hi', 'Hello');  
messageMap.set('greeting', 'How are you doing?');  
messageMap.set('bye', 'Bye');

for (let [key, value] of messageMap) {  
  console.log(`${key} - ${value}`);  
}
```

Objects and Maps are different in multiple ways. The keys of objects can only be of the string type. In Maps, keys can be of any value.

We can get the size of a Map easily with the `size` property which isn’t available in objects. The iteration order of Maps is in the iteration order of the elements, while the iteration of the keys of an object isn’t guaranteed.

Object has a prototype, so there are default keys in an object, but not in a Map.

Maps are preferred is we want to store non-string keys, when keys are unknown until run time, and when all the keys are the same type and values are the same type which may or may not be the same type as the keys.

We can use the `entries` function to get the entries of a Map. For example, we can write:

```javascript
let messageMap = new Map();  
messageMap.set('hi', 'Hello');  
messageMap.set('greeting', 'How are you doing?');  
messageMap.set('bye', 'Bye');

for (let [key, value] of messageMap.entries()) {  
  console.log(`${key} - ${value}`);  
}
```

To loop through the entries of the Map.

Keys are checked for equality by mostly following the rules of the triple equals operator, except that `NaN` is considered equal to itself and +0 and -0 are also considered equal. The algorithm is called the same-value-zero algorithm.

So, if we have:

```javascript
const map = new Map();  
map.set(NaN, 'abc');
```

Then we run `map.get(NaN)`, we get that `'abc'` returned. Also, it’s important to note that we can use non-string values as keys like we did above. For example:

```javascript
let messageMap = new Map();  
messageMap.set(1, 'Hello');  
messageMap.set(2, 'How are you doing?');  
messageMap.set(3, 'Bye');
```

If we call `messageMap.get(1)`, we get `'Hello'`.

However, since the keys are retrieved by checking with the triple equal operator with `NaN` considered equal to itself, we cannot get the value that you expect directly from object keys.

```javascript
let messageMap = new Map();  
messageMap.set({ messageType: 'hi' }, 'Hello');  
messageMap.set({ messageType: 'greeting' }, 'How are you doing?');  
messageMap.set({ messageType: 'bye' }, 'Bye');  
const hi = messageMap.get({  
  messageType: 'hi'  
})
console.log(hi);
```

`hi` will be `undefined` as it doesn’t check the content of the object for equality.

We can use array methods on Map if we convert it to an array first and then convert it back to a Map. For example, if we want to multiply all the values in a Map by 2, then we can write the following:

```javascript
let numMap = new Map();  
numMap.set('a', 1);  
numMap.set('b', 2);  
numMap.set('c', 3);

let multipliedBy2Array = [...numMap].map(([key, value]) => ([key, value * 2]));

let newMap = new Map(multipliedBy2Array);
console.log(newMap)
```

As we can see from the code above, we can use the spread operator to convert a Map to an Array directly by copying the values from the Map to the array.

Each entry consists of an array with the key as the first element and the value as the second element. Then we can call `map` on it since it’s an array. We can then multiply each value’s entry by 2, while keeping each entry’s array structure the same, with key first and value second.

Then we can pass it directly to the constructor of the `Map` object and we can see that the new Map object, `newMap`, has all the values multiplied by 2 while keeping the same keys as `numMap`.

Likewise, we can combine multiple Maps by first putting them in the same array with the spread operator and combining them into one, then passing it to the Map constructor to generate a new Map object.

For example, we can write:

```javascript
let numMap1 = new Map();  
numMap1.set('a', 1);  
numMap1.set('b', 2);  
numMap1.set('c', 3);  
numMap1.set('d', 3);  
numMap1.set('e', 4);  
numMap1.set('f', 5);  
numMap1.set('g', 6);  
numMap1.set('h', 7);  
numMap1.set('i', 8);  
numMap1.set('j', 9);  
numMap1.set('k', 10);  
numMap1.set('l', 11);let numMap2 = new Map();  
numMap2.set('m', 1);  
numMap2.set('n', 2);  
numMap2.set('o', 3);  
numMap2.set('p', 3);  
numMap2.set('q', 4);  
numMap2.set('r', 5);  
numMap2.set('s', 6);  
numMap2.set('t', 7);  
numMap2.set('u', 8);  
numMap2.set('v', 9);  
numMap2.set('w', 10);  
numMap2.set('x', 11);let numMap3 = new Map();  
numMap3.set('y', 1);  
numMap3.set('z', 2);  
numMap3.set('foo', 3);  
numMap3.set('bar', 3);  
numMap3.set('baz', 4);

const combinedArray = [...numMap1, ...numMap2, ...numMap3];  
const combinedNumMap = new Map(combinedArray);
```

When we log `combinedNumMap`, we see that we have all the original keys and values intact, but they were combined into one Map.

If we have overlapping keys, then the one that’s inserted later overwrites the values that were inserted earlier. For example:

```javascript
let numMap1 = new Map();  
numMap1.set('a', 1);  
numMap1.set('b', 2);  
numMap1.set('c', 3);  
numMap1.set('d', 3);  
numMap1.set('e', 4);  
numMap1.set('f', 5);  
numMap1.set('g', 6);  
numMap1.set('h', 7);  
numMap1.set('i', 8);  
numMap1.set('j', 9);  
numMap1.set('k', 10);  
numMap1.set('l', 11);

let numMap2 = new Map();  
numMap2.set('m', 1);  
numMap2.set('n', 2);  
numMap2.set('o', 3);  
numMap2.set('p', 3);  
numMap2.set('q', 4);  
numMap2.set('r', 5);  
numMap2.set('s', 6);  
numMap2.set('t', 7);  
numMap2.set('u', 8);  
numMap2.set('v', 9);  
numMap2.set('w', 10);  
numMap2.set('x', 11);let numMap3 = new Map();  
numMap3.set('y', 1);  
numMap3.set('z', 2);  
numMap3.set('a', 21);  
numMap3.set('b', 22);  
numMap3.set('c', 23);

const combinedArray = [...numMap1, ...numMap2, ...numMap3];  
const combinedNumMap = new Map(combinedArray);
```

When we log `combinedNumMap`, we see that we have most of the original keys and values intact, but they were combined into one Map. However, `a` now maps to 21, `b` now maps to 22, and `c` now maps to 23.

With Maps, we can have key-value pairs where the keys aren’t strings. Maps are collections that can be iterated in the order that the key-value pairs are inserted in.

They can be converted to arrays with each entry being an array with the key as the first element and the value as the second element, which means that we can convert them to arrays and then use arras operations like array methods.

We can use the spread operator to manipulate them in the way that we can do with any arrays, then convert them back to Maps again. We can use the `set` function to add more entries, and the `get` function to get the value of the given key.

The values are retrieved by the keys by the same-value-zero algorithm which means the keys are matched by the triple equals operator, with the exception that -0 and +0 are considered equal and `NaN` is considered equal to itself.