Introduction
Unlike many other programming languages, JavaScript's way of handling data collection is mostly with objects
and arrays
(don't forget, technically array is also a type of object in JavaScript). A majority of the developers rely on these heavily for slicing and dicing the data into a suitable data structure.
In the pre-ES6
era, there were not many options to deal with collections of data. Using an array
was a great way of achieving it. The combination of an array and object makes the data collection useful. But, there are some shortcomings with this:
- The key of an Object can only be of type string.
- The Object doesn't have a few important properties like knowing the
size
of it, or the flexibility ofiterating
through it. - The Object doesn't maintain the order of the elements in it.
ES6(ECMAScript 2015) brought us two new data structures, Map
and Set
to make the data collection more flexible and useful. In this article, we will go over the Map data structure to learn how to use it in practice.
Maps
A Map is a collection of key-value pairs where the key can be of any type. The Map remembers the original insertion order of the elements. It means the data from the Map will be retrieved in the same order that it was inserted.
If you notice closely, Map has characteristics of both Object and Array.
- Object characteristic - Supports the key-value pair structure.
- Array characteristic - Remembers the insertion order.
Create and Initialize a Map
A new Map can be created as,
const map = new Map();
It returns an empty Map.
Map(0) {}
A point to note here. A newly created Map
has no default keys. Whereas, if you create a JavaScript object to make it work like a Map, it would inherit properties from its prototype.
Another way of creating a Map
is with initial values. Here we are creating a Map initializing with three key-value
pairs,
const greenrootsBlog = new Map([
['name', 'greenroots'],
['type', 'blog'],
['writer', 'Tapas Adhikary'],
]);
It returns a Map with three elements,
Map(3)
{
"name" => "greenroots",
"type" => "blog",
"writer" => "Tapas Adhikary"
}
Add values to the Map
To add value to a Map, use the set(key, value)
method. The set(key, value)
method takes two parameters, key
and value
, where the key and value can be of any type, primitives(boolean
, string
, number
etc) or an object.
// create a map
const map = new Map();
// Add values to the map
map.set('name', 'greenroots');
map.set('type', 'blog');
map.set('writer', 'Tapas Adhikary');
Output,
Map(3)
{
"name" => "greenroots",
"type" => "blog",
"writer" => "Tapas Adhikary"
}
Please note, if you use the same key to add values multiple times to a Map, it will always replace the last value.
// Add a different writer
map.set('writer', 'Someone else!');
Now the map output will be,
Map(3)
{
"name" => "greenroots",
"type" => "blog",
"writer" => "Someone else!"
}
Getting values from the Map
You must have guessed it by now. Yeah, Map has a method called, get(key)
for getting value from it by passing a key
.
map.get('name');
map.get('type');
map.get('writer');
Please note, the get(key)
method returns an undefined
if a non-existing key is passed to it.
Map Keys
A prominent difference of an object and the Map is, Map keys can be of any type. Let us see it with examples.
// create a Map
const funMap = new Map();
funMap.set(360, 'My House Number'); // number key
funMap.set(true, 'I write blogs!'); // boolean key
let obj = {'name': 'tapas'}
funMap.set(obj, true); // object as key
console.log(funMap);
Output,
Map(3)
{
360 => "My House Number",
true => "I write blogs!",
{…} => true
}
Now,
funMap.get(360); // returns, 'My House Number'
funMap.get(obj); // returns, true
funMap.get('360'); // undefined
A regular JavaScript object always treats the keys as strings. Even when you pass them as other primitives or objects, it internally converts the keys to strings. Here is an example to understand it,
// Create an empty object
const funObj = {};
// add a property. Note, passing the key as a number.
funObj[360] = 'My House Number';
// It returns true.
// Because the number 360 got converted to a string '360' internally!
console.log(funObj[360] === funObj['360']);
Map properties and methods
The Map has built-in properties and methods that make it so powerful and flexible to use. Let's create a Map to explain them.
const map = new Map();
map.set('John', 34);
map.set('Alex', 15);
map.set('Buddy', 37);
Know the size of a Map
Use the size
property of the Map to know how many elements are in it.
console.log('size of the map is', map.size);
It reruns the number of elements in a Map. In this case, it will be 3.
Please note: Just like array's
length
,size
is also a property, not a method.
Find an element with has()
The method has(key)
returns true if the Map has an element with the key.
console.log(map.has('John')); // returns, true
console.log(map.has('Tapas')); // returns, false
Remove an element with, delete()
We can delete an element from the map using the delete(key)
method.
map.delete('Buddy'); // removes the element with key, 'Buddy'.
Clear the entire Map
Use the clear()
method to remove all the elements at once from the Map.
// Clear the map by removing all the elements
map.clear();
map.size // It will return, 0
MapIterator - Keys(), values(), entries()
All the methods(except clear()
) we have seen so far, is to deal with the key-value of a Map one-by-one. There are three useful methods to get all the keys, values, and key-value pairs respectively.
These methods return a MapIterator
which is excellent because you can do a for-of
or forEach
loop directly on it.
First, create a Map,
const ageMap = new Map([
['Jack', 20],
['Alan', 34],
['Bill', 10],
['Sam', 9]
]);
Get all the keys
console.log(ageMap.keys());
Output,
MapIterator {"Jack", "Alan", "Bill", "Sam"}
Get all the values
console.log(ageMap.values());
Output,
MapIterator {20, 34, 10, 9}
Get all the entries(key-value pairs)
console.log(ageMap.entries());
Output,
MapIterator {"Jack" => 20, "Alan" => 34, "Bill" => 10, "Sam" => 9}
Iterating over a Map
There are multiple ways of iterating over a Map. You can use, forEach
or for-of
loop to iterate over it.
With forEach
// with forEach
ageMap.forEach((value, key) => {
console.log(`${key} is ${value} years old!`);
});
Note, the first argument is the value and the second is the key. The output is,
Jack is 20 years old!
Alan is 34 years old!
Bill is 10 years old!
Sam is 9 years old!
With for-of
We can simply destructure
the keys and values from the Map using the for-of
loop.
for(const [key, value] of ageMap) {
console.log(`${key} is ${value} years old!`);
}
Object to Map
You may encounter a situation where you need to convert an object
to a Map
like structure. You can use the method, entries
of Object
to do that.
const address = {
'Tapas': 'Bangalore',
'James': 'Huston',
'Selva': 'Srilanka'
};
const addressMap = Object.entries(address);
Map to Object
If you want to do the reverse, you can use the method called, fromEntries
.
Object.fromEntries(map)
Map to Array
There are a couple of ways to convert a map to an array.
- Using
Array.from(map)
const map = new Map();
map.set('milk', 200);
map.set("tea", 300);
map.set('coffee', 500);
console.log(Array.from(map));
Output,
- Using the spread operator
We can use the spread operator
as well to convert a Map to an array.
console.log([...map]);
Map vs Object: When to use what?
Map
has characteristics of both object
and array
. However, Map is more like an object
than array
due to the nature of storing data in the key-value
format.
The similarity with the object ends here though. The Map is very different from the object in many other ways as we have seen so far. So, which one to use, when? How to take that call?
Use Map when
- Your need is not simple. You may want to create keys that are non-strings. Storing an object as a key is a very powerful approach. The Map gives it to you by default.
- You need a data structure where elements can be ordered. Objects do not maintain the order.
- You are looking for flexibilities without relying on an external library like
lodash
. You may end up using a library likelodash
because we do not find methods like,has()
,values()
,delete()
or a property like,size
with the object.
Map
makes it easy for you by providing them by default.
Use Object when
- You do not have any needs like the above.
- You rely on
JSON.parse()
as, aMap
cannot be parsed with it.
To end it
Hope it was a useful explanation of the Map
data structure in JavaScript. Give it a try if you are not using it already.
You may also like other JavaScript-related articles,
- Build your JavaScript Muscles with map, reduce, filter and other array iterators
- Explain Me Like I am Five: What are ES6 Symbols?
- JavaScript: Equality comparison with ==, === and Object.is
- My Favorite JavaScript Tips and Tricks
If it was useful to you, please Like/Share so that, it reaches others as well and feel free to follow me on twitter @tapasadhikary.
Top comments (2)
I like Maps, but still have to find a real-word use case to use them. Maybe we're too much used to the Object structure so that we think in objects?
In my project, the data structure demands to use the object itself as a key. We found the Map to be extremely useful there. What I found is, wherever objects can be used(because the use-cases are simple), use objects. The map is purely its use-cased based where the object is lacking(or too much to way around to make it work).
Thank you very much for reading the article and discussing it! Appreciate.