5 Reasons To Choose JavaScript Maps Over Objects For Storing Key-Value Pairs(With Examples)
Tags: javascript, webdev, programming, beginners
Published: 2023-01-21 09:51:29

At 18 I started my "career" as an IT support guy.
At 20 I was flying 80 hours per month as a flight attendant.
At 22 I got my commercial airplane pilot license.
At 29 I was a carpenter and today at 32 I work as a software developer.

If there's one thing I know how to do well, it is how to keep up with changes. Because if you don't, you are left behind. I know firsthand how comfortable it is to use stuff you know by heart instead of giving the 'new' a try.

But discomfort is what makes you grow, as a human and as a dev.

Let's dive head first and see what the "newish" `Map` can teach us today.

## What is the Map object?

Here's the holy source's (MDN) definition:

The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.

To put it simply, Map is JavaScript's native hash or dictionary data structure.


## Syntax: Map vs Object

Here's a handy cheat sheet that does a great job at showing their syntax differences, posted by [Andrej](https://twitter.com/reactive_dude) on Twitter.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwrkz5px2ahdxa6i9d7b.png)

He also recorded [performance tests](https://www.youtube.com/watch?v=MH22Tuf65DI&t=2s) if you are interested.


## Reason 1: You Won't Overwrite Default Keys By Accident

**Map**
By default, a Map does not contain any keys. It is a clean slate. It only has what you put into it. 

Neither more nor less.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qo5hzu3hvfohw7koppd.png)


**Object**
Objects by default have their prototype, so it contains default keys that could potentially clash with your own keys.

This can be bypassed by passing `null` as the prototype when creating a new Object: `Object.create(null)`

Check out the difference:

* New Object

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43wav4nctn3w3qeglpya.png)

* New Object with `null` prototype

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j876nfoletesbk63mp3v.png)


## Reason 2: It Accepts Any Type As Key

**Map**
Maps accept **any** type of key. That includes functions, objects, and all primitives(string, number, boolean, symbol, undefined, null, and bigint).	

```javascript
let obj = { 'a': 'a' };
let func = () => 'hey';

//you can also initialize multiple values at once using array syntax
let map = new Map([[123, true], [true, 123], [obj, 'object'], [func, 'function']])

map.keys() // 123, true, Object, () => 'hey'
map.get(obj) // 'object'
map.get(func) // 'function'
map.get({ 'a': 'a' }) // undefined 
//Object and Functions are stored by reference, so { 'a':'a' } and obj are different objects)
```

**Object**
Object's key must be either a String or a Symbol.

```javascript
let obj1 = { 'a': 'a' };
let func = () => 'hey';

let obj = { 123: true, true: 123, obj1: 'object', func: 'function' };
Object.keys(obj) 
// ['123', 'true', 'obj1', 'func'] converts all keys to strings

obj[func] //undefined
obj['func'] // 'function'
``` 

## Reason 3: Maps Are Iterable

**Map**
Maps are natively iterable. That means you can loop over them with `for of` or `.forEach()` loops. 

```javascript
const map = new Map();
map.set(0, 'zero').set(1, 'one'); //you can chain .set()

for (const [key, value] of map) {
  console.log(`key: ${key}, value: ${value}`);
}
// key: 0, value: zero
// key: 1, value: one

//if you just want the 'values' or just the 'keys'
for (const key of map.keys()) { // or map.values()
  console.log(key);
}
// 0
// 1

map.forEach((value, key) => console.log(`key: ${key}, value: ${value}`));
// key: 0, value: zero
// key: 1, value: one
```

**Object**
Objects are not iterable, even though you can iterate over them using `for in` and `Object.Entries()`

```javascript
let obj = { 0: 'zero', 1: 'one' }

for(let key in obj){
    console.log(`key: ${key}, value: ${obj[key]}`)
}
// key: 0, value: zero
// key: 1, value: one

Object.entries(obj).forEach((item) => console.log(`key: ${item[0]}, value: ${item[1]}`))
// key: 0, value: zero
// key: 1, value: one

```

## Reason 4: Maps Can Be Merged With Arrays, And Converted To Arrays

**Map**
Maps are Arrays are 100% compatible, making it intuitive move from one to another.

Here's how you can convert a `Map` into an `Array`
```javascript
let map = new Map([ [1, 'one'], [2, 'two'] ]);
Array.from(map) //[ [1, 'one'], [2, 'two'] ] exactly the same array you initially passed in

//or you can use the spread operator
const newArr = [...map];
```

An `Array` into a `Map`
```javascript
let arr = [ [1, 'one'], [2, 'two'] ];
new Map(arr); //{ 1 => 'one', 2 => 'two' }
```

Here's how you can **merge** a `Map` and an `Array`
```javascript
let map = new Map([ [1, 'one'], [2, 'two'] ]);
let arr = [3, 'three']

let combinedMap = new Map(...map, arr); 
// { 1 => 'one', 2 => 'two', 3 => 'three' }

let combinedArr = [...map, arr];
// [ [1, 'one'], [2, 'two'], [3, 'three'] ]
```

**Object**
Concerting an `Object` into an `Array`
```javascript
let obj = { 1: 'one', 2: 'two'};
Array.from(obj) // [] doesn't work

//you'd have to do
Array.from(Object.entries(obj))
//[ ['1', 'one'],['2', 'two'] ]

//or
[...Object.entries(obj)]
//[ ['1', 'one'],['2', 'two'] ]
```


## Reason 5: You Can Easily Check The Size

**Map**
Map has a built-in `size` property that returns its size.
```javascript
let map = new Map([1, 'one'], [true, 'true']);
map.size // 2
```

**Object**
To check the size of an object you have to combine `Object.keys()` with `.length`
```javascript
let obj = { 1: 'one', true: 'true' };
Object.keys(obj).length // 2
```


## The downside? No Native Method For Serialization And Parsing

**Map**
Maps have no native support for serialization or parsing to and from JSON.

The documentation suggests implementing your own by using the `replacer` argument that can be passed to `JSON.stringify(obj, replacer)` and the `reviver` argument passed to `JSON.parse(string, reviver)`

You can find the suggested implementation [here](https://stackoverflow.com/questions/29085197/how-do-you-json-stringify-an-es6-map)

**Object**
You can natively serialize and parse an Object to and from JSON using `JSON.stringify()` and `JSON.parse()` respectfully. 


## 2 Examples of how you can replace `Object` with `Map`

**#1** Calculating the total price and items of an eCommerce shopping cart

This is one of the examples from the [Complete Guide To JavaScript .reduce() Function](https://dev.to/gustavupp/a-complete-guide-to-javascripts-reduce-function-with-real-world-examples-5h6k) 

Given the following array
```javascript
const shoppintCart = [
  { price: 10, amount: 1 },
  { price: 15, amount: 3 },
  { price: 20, amount: 2 },
]
```

We want to return an `Object` like { totalItems: 6, totalPrice: 45 }

This is the original code
```javascript
shoppintCart.reduce(
  (accumulator, currentItem) => {
    return {
      totalItems: accumulator.totalItems + currentItem.amount,
      totalPrice:
        accumulator.totalPrice + currentItem.amount * currentItem.price,
    }
  },
  { totalItems: 0, totalPrice: 0 } //initial value object
)
// { totalItems: 6, totalPrice: 45 }
```

This is a version using `Map`
```javascript
shoppintCart.reduce(
  (accumulator, currentItem) => {
      accumulator.set('totalItems', accumulator.get('totalItems') + currentItem.amount);
      accumulator.set('totalPrice', accumulator.get('totalPrice') + currentItem.price);
      return accumulator;
  },
  new Map([['totalItems', 0], ['totalPrice', 0]])
)
// { 'totalItems' => 6, 'totalPrice' => 45 }
```

**#2** A Different Way To Remove Duplicate Objects From An Array

This is a piece of code I actually used in the [bookshelf app](https://gustavupp.github.io/book-lookUp/dist/index.html) I made when I started learning JavaScript.

Googling about how to remove duplicate objects from an array led me to the example I share below.

This is an array with one duplicate object
```javascript
const books = [
  { id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie' },
  { id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie' },
  { id: 2, title: 'The Alchemist', author: 'Paulo Coelho' },
]
```

Here's a peculiar way to remove the duplicate:
```javascript

const uniqueObjsArr = [
   ...new Map(books.map(book => [book.id, book])).values()
];
```

There is a bit too much happening on the above one-liner.
Let's break it down into chunks so it is easy to digest.

```javascript

// 1. map the array into an array of arrays with `id` and `book`

const arrayOfArrays = books.map(book => [ book.id, book ])
// arrayOfArrays:
/*
[ 
   [ 1, {id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie' } ],
   [ 1, {id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie' } ],
   [ 2, { title: 'Alchemist', author: 'Paulo Coelho' } ]
]
*/

// 2. The duplicate is automatically removed as keys have to be unique

const mapOfUniqueObjects = new Map(arrayOfArrays)
// mapOfUniqueObjects: 
/*
{
   1 => {id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie'},
   2 => { title: 'Alchemist', author: 'Paulo Coelho' }
}
*/

// 3. Convert the values back into an array.

const finalResult = [...mapOfUniqueObjects.values()];
// finalResult:
/*
[
   {id: 1, title: 'How To Win Friends And Influence People', author: 'Dale Carnegie'},
   {id: 2, title: 'The Alchemist', author: 'Paulo Coelho'}
]
*/
```

## Conclusion

If you need to store key-value pairs(hash or dictionary) use `Map`.

If you're only using string-based keys and need maximum read performance, then objects might be a better choice.

Other than that, use whatever you want because, at the end of the day, this is just a blog post of a random dude on the internet Lol.

**Here's what we covered:**

* What is the Map object?
* Syntax: Map vs Object
* Reason 1: You Won't Overwrite Default Keys By Accident
* Reason 2: It Accepts Any Type As Key
* Reason 3: Maps Are Iterable
* Reason 4: Maps Can Be Merged With Arrays, And Converted To Arrays
* Reason 5: You Can Easily Check The Size
* The downside? No Native Method For Serialization And Parsing
* 2 Examples of how you can replace `Object` with `Map`

Thanks for reading!

If you like this article:

*Leave a comment below(You can just say hi!)
*Follow me on Twitter[@theguspear](https://twitter.com/theguspear)

Catch you later,

Gus.