Why to use Maps over Objects in JS ?
Tags: javascript, react, webdev, beginners
Published: 2021-10-07 04:57:24

In JavaScript, an object is a standalone entity, with properties and type. 

Compare it with a cup, for example. A cup is an object, with properties. A cup has color, design, weight, and the material it is made of, etc. 

___

![Enough talk](https://i.giphy.com/media/QJvwBSGaoc4eI/giphy.webp)
___

## Table Of Contents

  * [Problems I faced while working with objects](#chapter-1)
  * [Solution: Maps Data Structure](#chapter-2)
  * [Comparison: Objects and Maps](#chapter-3)
  * [Practical Example](#chapter-4)
  * [Problems in Maps] (#chapter-5)
  * [Conclusion] (#chapter-6)

### 1. Problems I faced while working with objects: <a name="chapter-1"></a> 

* #### Only string or symbol could be used as key.
  - Objects have a limitation that their keys have to be strings 
    ```javascript
       const names = {
         1: 'One',
         2: 'Two',
       };
       Object.keys(names); // => ['1', '2']
    ```
  - The numbers `1` and `2` are keys in the names object. Later, when the object’s keys are accessed, it turns out that the `numbers were converted to strings`.
  - Implicit conversion of keys is tricky because you `lose the consistency of the types.`

* #### No proper helper methods to work with objects.
  - In order to find the length of the object we need to 
    either use `Object.keys()` or `Object.values()` and then 
    find the length by accessing `.length` on the array 
    returned.
  - Similarly to iterate over it we have to use the same 
    methods above to perform an iteration over the object.

* #### Own object properties might collide with property keys inherited from the prototype (e.g. toString, constructor, etc).
  - Any object inherits properties from its prototype object.
  - The accidentally overwritten property inherited from the prototype is dangerous. Let’s study such a dangerous situation.
  - Let’s overwrite the toString() property in an object actor:
    ```javascript
       const actor = {
        name: 'Harrison Ford',
        toString: 'Actor: Harrison Ford' 
       /* 
       this will cause a problem since we are overriding the 
       toString method of the prototype chain
       */
      };
    ```

* ####Deleting keys causes problem in large objects.
  - Using delete causes various forms and magnitudes of a slowdown in many situations, because it tends to make things more complicated, forcing the engine (any engine) to perform more checks and/or fall off various fast paths.


### 2. Solution: Using Maps Data Structure<a name="chapter-2"></a> 

Maps is a collection of keyed data items, just like an object. But the main difference is that Map allows keys of any type.

Methods and properties are:

* `new Map()` – creates the map.
* `map.set(key, value)` – stores the value by the key.
* `map.get(key)` – returns the value by the key, undefined if the key doesn’t exist in map.
* `map.has(key)` – returns true if the key exists, false otherwise.
* `map.delete(key)` – removes the value by the key.
* `map.clear()` – removes everything from the map.
* `map.size` – returns the current element count.

Code:

```javascript
let map = new Map();

map.set('1', 'str1');   // a string key
map.set(1, 'num1');     // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3
```

Maps have useful and intuitive helper methods which are used to perform different operations.


### 3. Comparison: Objects and Maps<a name="chapter-3"></a> 

| Parameters    | Object      |    Maps    |
| ------------- |:-----------:|:---------:|
| Iteration | Object does not implement an iteration protocol, and so objects are not directly iterable using the JavaScript for...of statement (by default). | A Map is an iterable, so it can be directly iterated |
| Key Types| The keys of an object must be either a String or a Symbol.| A Map's keys can be any value (including functions, objects, or any primitive). |
| Size |   The number of items in an object must be determined manually.  |     The number of items in a Map is easily retrieved from its size property. |
| Performance | Not optimized for frequent additions and removals of key-value pairs.  |    Performs better in scenarios involving frequent additions and removals of key-value pairs. |


### 4. Practical Example<a name="chapter-4"></a> 

Let's take an example of implementing **select all** functionality.

![Select All](https://i.stack.imgur.com/mT4Ry.png)

```javascript
const selectedItems = {}; 
// we will use object here for quick lookup since its search is O(1)

// adding item into selectedItems
selectedItems['image/png'] = true 
selectedItems['text/html'] = true

// removing item from selectedItems
selectedItems['application/pdf'] = false 
```

The code seems simple, but if you notice we are not deleting the key here we are setting it to false.

![Screenshot 2021-10-06 at 1.49.49 PM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i45o7j0rmgriqxzsm71r.png)

So in order to change the header selection state either from `partial` to `complete` or vice versa.We need to traverse over the object and detect false and true values.

It would have been easy if we could have deleted items from an object and then had checked the length of the object to determine if the current state is `partial` or `complete`.

But delete has performance issues in our V8 engine especially when we want to do multiple deletions of keys.

**Maps comes to the rescue, map has delete functionality as well as functionality to return size, unlike object where we need to convert to array and then find the length of it. All without causing performance bottleneck.**

```javascript
const selectedItems = new Map()

// adding item into selectedItems
selectedItems.set('image/png') 
selectedItems.set('text/html')

// removing item from selectedItems
selectedItems.delete('application/pdf')
```

One of the solutions was to set **selectionItems** to `{}` when we want to remove all the selected items, but that is not a scalable solution in certain situations.

When we do pagination in a table we have scenarios where `select-all` is performed to items specific to the current page and not on the items of the next or previous page.

In this case, if we set `selectedItems = {}` it will reset all the values, which is an incorrect solution. 

Hence, maps are more scalable solution since it does not face any problem with respect to the deletion of the key.

### 5. Problems in Maps<a name="chapter-5"></a> 

* ####Maps is not here to replace objects
  - If we are only using string-based keys and need maximum read performance, then objects might be a better choice. 
  - This is because Javascript engines compile objects down to C++ classes in the background, and the access path for properties is much faster than a function call for Map().get().
  - Adding or removing a property causes the shape of the class to change and the backing class to be re-compiled, which is why using an object as a dictionary with lots of additions and deletions is very slow, but reads of existing keys without changing the object are very fast.

* ####Maps are not serializable
  - Maps does not have native support for serialization or parsing
  - Redux does not recommend using non-serializable data structures, since it can break the working of dev-tools and also will cause problems in rendering updates as expected: https://redux.js.org/style-guide/style-guide#do-not-put-non-serializable-values-in-state-or-actions

### 6. Conclusion<a name="chapter-6"></a>

In review, while we will still rely heavily on JavaScript objects to do the job of holding structured data, they have some clear limitations

These limitations are solved by maps. Moreover, maps provide benefits like being iterators and allowing easy size look-up.
 
Objects are not good for information that’s continually updated, looped over, altered, or sorted. In those cases, use maps. 

In conclusion, use maps with a purpose. Think of maps and objects similar to how let and const are used for our variables. 

![Enough talk](https://i.giphy.com/media/BYoRqTmcgzHcL9TCy1/giphy.webp)