You're probably familiar with Javascript Objects - 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
:
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:
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:
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()
:
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()
:
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:
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:
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:
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:
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:
let myFirstMap = new Map();
let myFunction = function() { return "someReturn"; }
myFirstMap.set(myFunction, "value");
Map keys are based on reference, not value. That means although the following will work:
let myFirstMap = new Map();
let myFunction = function() { return "someReturn"; }
myFirstMap.set(myFunction, "value");
myFirstMap.get(myFunction); // Returns "someReturn"
This will not:
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:
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:
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:
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:
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.
For example, let's look at how entries()
works:
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:
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:
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:
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()
:
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.
Hope you've enjoyed this one - have a great day.