A Complete Guide to Javascript Maps
Tags: javascript, webdev, tutorial, typescript
Published: 2022-09-27 20:30:07

You're probably familiar with [Javascript Objects](https://fjolt.com/article/javascript-objects-introduction) - but did you know there is another way to create sets of data in Javascript, known as **Maps**? You might be using Javascript plain old objects right now, when a map may be a better solution to your problem. 

Javascript maps differ in a few major ways to objects. Although `typeof new Map()` returns `object`, don't let that fool you! Here are some of the major differences from objects:

- **Does not contain any keys by default**, unlike objects, which typically contain a `prototype` object unless created in a specific way.
- **They are guaranteed to be ordered by the order they were inserted**. Objects are like this too these days, but they don't carry the same guarantee.
- **The keys of a map can be anything**, including a function, or even an object. Whereas in Javascript, it must be a string or symbol.
- **They have better performance than objects on tasks which require rapid or frequent removal or addition of data**.
- **They are iterable** by default, unlike objects.

Given that maps have so many benefits, let's take a look at how they work.

## The Basics of how Javascript Maps work
Any map in Javascript is initiated using the `new Map()` constructor. For example, let's create a map called `myFirstMap`:
```javascript
let myFirstMap = new Map();
```

The difference is to set, get, or delete keys from a map, you have to use specific methods that come along with `Map`. So to set a new value of `someValue` with the key `firstKey`, I can run the following method:
```javascript
let myFirstMap = new Map();

myFirstMap.set('firstKey', 'someValue');
```

### Deleting an item in a Javascript Map
If we then wanted to delete a key in a Javascript map, we have to call the `delete()` method:
```javascript
let myFirstMap = new Map();

myFirstMap.set('firstKey', 'someValue');
myFirstMap.delete('firstKey');
```

You can also delete the entire map altogether, leaving no items in it using `clear()`:
```javascript
let myFirstMap = new Map();

myFirstMap.set('firstKey', 'someValue');
myFirstMap.clear();
console.log(myFirstMap); // Returns Map(0)
```

### Getting a key in a Javascript Map
Similar in usage to the other methods, to get the value of `firstKey`, we have to use `get()`:

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

myFirstMap.set('firstKey', 'someValue');

myFirstMap.get('firstKey') // 'someValue'
```

### Checking if a key exists in a Javascript Map

Javascript Maps also have a method called `has()`, if we want to check if a map has a certain key:
```javascript
let myFirstMap = new Map();

myFirstMap.set('firstKey', 'someValue');

myFirstMap.has('firstKey') // true
```

### Warning: don't use typical Object properties with Maps

Javascript is full of quirks, and maps are no different. Weirdly, maps can also support object notation. For example, this seems to work:
```javascript
let myFirstMap = new Map();

myFirstMap['firstKey'] = 'someValue';

console.log(myFirstMap); // Map(0) { firstKey: 'someValue' }
```

**However, you should not do this!** This is **not** creating new entries in the map itself - you're simply creating an object. So you'll lose all the benefits of Javascript maps.

## Telling how big a Javascript Map is

One other useful thing where maps are a little easier to use than objects is finding out how many keys are in a map. For this, we can use the `size()` method, which returns the number of keys:
```javascript
let myFirstMap = new Map();

myFirstMap.set('firstKey', 'someValue');

myFirstMap.size // 1
```

For objects, we typically use a mixture of `Object.keys()` and `length` to find out the size of an object:
```javascript
let myObj = { "name" : "John" };
let sizeOfObj = Object.keys(myObj).length; // 1
```

## Using Maps with non-string keys

As I mentioned, Javascript Maps allow non conventional keys, like functions and objects, whereas objects only allow strings and symbols. For example, this is valid in a map:
```javascript
let myFirstMap = new Map();
let myFunction = function() { return "someReturn"; }
myFirstMap.set(myFunction, "value");
```

Map keys are [based on reference, not value](https://fjolt.com/article/javascript-by-reference-by-value). That means although the following will work:
```javascript
let myFirstMap = new Map();
let myFunction = function() { return "someReturn"; }
myFirstMap.set(myFunction, "value");

myFirstMap.get(myFunction); // Returns "someReturn"
```

This will not:
```javascript
let myFirstMap = new Map();
let myFunction = function() { return "someReturn"; }
myFirstMap.set(myFunction, "value");

myFirstMap.get(function() { return "someReturn"; }); // Returns undefined
myFirstMap.get('someReturn'); // Returns undefined
```

That's because although `function() { return "someReturn"; }` and `myFunction` are the same in value, they are not the same in terms of where they are stored in system memory. So they are not exactly equivalent. Similarly, maps do not work on return value - so `myFirstMap.get('someReturn')` also returns undefined.

The same example works for objects, with similar results:
```javascript
let myFirstMap = new Map();
let myObject = { "someKey" : "someValue" }
myFirstMap.set(myObject, "value");

myFirstMap.get({ "someKey" : "someValue" }); // Returns undefined
myFirstMap.get(myObject); // Returns 'value'
```

## Merging Javascript Maps

If you have multiple maps you want to merge into one, you can merge them in the same way you merge objects - with the spread syntax. For example, here I merge `myFirstMap` and `mySecondMap` into `myNewMap` using the spread syntax:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
let mySecondMap = new Map();
mySecondMap.set("someOther", "value");

let myNewMap = new Map([...myFirstMap, ...mySecondMap]);

console.log(myNewMap);
// Map(2) { some: "value", someOther: "value" }
```

## Iterating on a Map
As mentioned, maps are **iterable** by default. If we want to iterate over objects we usually have to use a function like `Object.keys`. Ultimately this means we can use `forEach` on any map, like so:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");

myFirstMap.forEach(function(value, key, map) {
    // value -> the value of that key in the map
    // key -> the key for this item in the map
    // map -> the entire map
    console.log(value, key, map);
})
```

### Iterating on a Javascript Map using for
You can also iterate on a map using `for(let ... of )`! If you do that, each item is returned as an array of the key and value. For example:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");

for(let x of myFirstMap) {
    // Returns [ 'some', 'value' ]
    console.log(x);
}
```

### Iterating over values or keys in Javascript Maps
Another cool way we can iterate over values or keys in Javascript is to use the `values()` or `entries()` methods. These return a **new iterator** for the values and items in a map respectively. That means we can access the next key or value using `next()` functions, just like in [generator functions](https://fjolt.com/article/javascript-generator-functions).

For example, let's look at how `entries()` works:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");
myFirstMap.set("aFinal", "value");

let allKeys = myFirstMap.entries();
console.log(allKeys); // Returns MapIterator {} object

console.log(allKeys.next()); // Returns { value: [ 'some', 'value' ], done: false }
console.log(allKeys.next().value); // Returns [ 'some', 'value' ]
```

Our return from `allKeys.next()` is an object. The value in this object is `[ 'some', 'value' ]` - an array of the first item in our map. We can keep running `next()` to get the following items in the map. Pretty cool! We can do the same thing again, with just values:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");
myFirstMap.set("aFinal", "value");

let allValues = myFirstMap.values();
console.log(allValues); // Returns MapIterator {} object

console.log(allValues.next()); // Returns { value: 'value' done: false }
console.log(allValues.next().value); // Returns 'value'
```

Iterators like this prove useful in some specific situations, and can be a cool way to iterate through all the data in your Map.


## Serialization of Maps in Javascript

One of the drawbacks for some people which maps have, is that they cannot be easily serialized with `JSON.parse()` and `JSON.stringify`. Trying to do so results in an empty object, and this kind of makes sense - since the object of a Map is empty, if we only populate it with entries:

```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");
myFirstMap.set("aFinal", "value");

// Returns {}
console.log(JSON.stringify(myFirstMap));
```

The only realistic way to serialize a Map is to convert it to an object or array, which simply means you'll have to maintain some separate helper functions to do this task for you, should you use Maps. For example, we can use `Array.from()` to convert our Map to an array, and then use `JSON.stringify()` to serialize it:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");
myFirstMap.set("aFinal", "value");

let arrayMap = Array.from(myFirstMap);

// Returns [["some","value"],["someOther","value"],["aFinal","value"]]
console.log(JSON.stringify(arrayMap));
```

Then, if we want to turn it back into a map, we have to use `JSON.parse()` in conjunction with `new Map()`:
```javascript
let myFirstMap = new Map();
myFirstMap.set("some", "value");
myFirstMap.set("someOther", "value");
myFirstMap.set("aFinal", "value");

// Turn our map into an array
let arrayMap = Array.from(myFirstMap);

// The JSON stringed version of our map:
let stringifiedMap = JSON.stringify(arrayMap);

// Use new Map(JSON.parse...) to turn our stringed map into a map again:
let getMap = new Map(JSON.parse(stringifiedMap));

// Returns Map(3) {'some' => 'value', 'someOther' => 'value', 'aFinal' => 'value'}
console.log(getMap);
```

## Conclusion

Javascript Maps are a great way to store data, when you don't need all the flexibility of objects, and things like the order of your data are incredibly important. They also prove more performant than objects in situations requiring frequent addition and removal of items. In this guide we've covered everything you need to know about Maps, but if you want to [learn more about Javascript click here](https://fjolt.com/category/javascript).

Hope you've enjoyed this one - have a great day.