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 on Twitter.
He also recorded performance tests 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.
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
- New Object with
null
prototype
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).
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.
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.
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()
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
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
let arr = [ [1, 'one'], [2, 'two'] ];
new Map(arr); //{ 1 => 'one', 2 => 'two' }
Here's how you can merge a Map
and an Array
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
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.
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
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
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
Given the following array
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
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
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 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
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:
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.
// 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
withMap
Thanks for reading!
If you like this article:
*Leave a comment below(You can just say hi!)
*Follow me on Twitter@theguspear
Catch you later,
Gus.
Top comments (34)
Hi Gus,
Using a Map over an Object is very often a wise choice but for some reason one I seldom employ; I have no justification other than set in my ways.
One thing to consider is JSON, Objects are supported directly but Maps are not.
Regards, Tracy
Hey Tracy! Good to see you here.
The same goes for me. That is one of the reasons why I wrote the post haha.
There is also the case where most of your colleagues might not be used to it, than you'd prefer to stick with regular objects not to raise eyebrows haha..
Thanks for stopping by again, and have a great week!
Isn't speed/performance another reason to prefer Map? I've heard that Map is faster with lookups, especially when the number of items is larger.
Hey Leopb! how are you? thanks for leaving a comment!
You're right, Map seems to be faster in most cases, specially for reading.
There is a video in the first section with some tests.
Have a great week my dude!
The video doesn't actually cover a
map.get(key: string)
vsobjectMap.key
access comparison.Given the impact that performance of the
object.property
lookup has on the overall JS runtime performance I would be surprised ifmap.get(key: string)
is faster thanobject.property
for lookup over a[key, value]
set not exceeding the size of the number of properties found on a typical object.Map is optimized for
[key, value]
deletion and insertion (while maintaining the insertion order for iteration which is something objects per spec don't guarantee even though they often do) and efficiently handling larger[key, value]
sets.Meanwhile an object literal lookup will often perform similar to a
switch
statement on a string value.Hey Peerreynders! Thanks for you insighful comment!
Apperantly Map outperforms Object in all operations unless you have small integer, array-indexed keys according to this article that run multiple tests.
Have an awesome week!
The article discusses insertion, deletion, and iteration performance, the scenarios Map is specifically optimized for. Lookup/access performance isn't discussed.
In this test objects are faster on Chrome 109 (V8) while maps are faster on Firefox 109.0 (SpiderMonkey). But once the number of entries increases to 20-30 map performance catches up.
I didn't know about the jsbench website! Thanks for sharing the link Peerreynders!
That is an in-depth test.
Thanks once again.
Great post! No native serialization is a big downside though. In modern apps, a lot of data structures go from/to Json APIs, so having data structures with native serialization is extremely convenient. The benefits are there, but only for data that doesn't need to be serialized I think.
Hey Rense! thanks for stopping by.
100% with you, no native serialization/parsing is a big downside. That's one of the reasons I'd choose plain objects.
Have a great week!
Great post, it's clear that you have a lot of experience and knowledge about the topic. Your examples and explanations make it easy to understand the differences between Map and Object.
Have you tried experimenting with the different use cases of Map and Object in your projects? It would be great to see some examples of how you have utilized these data structures in your own work.
Hey Elliot! how are you? Thanks for taking the time to comment!
I am not that experienced at all lol. I am just curious.
And yes I have used it a few times. The second example at the end of the post is actual code I used in a project I build when buidling my portfolio.
Have a great week man!
Very nice article, thanks a lot! I will use
Map
and see how that goes.I am happy that you liked it Franco! Come back and let me know how it went
Great research and article π
Thanks a lot Rai! Happy to hear it
Great post with good examples and explanations
I am super glad you found it useful Easyjobber!
Passe une bonne semaine!
map is more like an array and modern data type.
Thanks for commenting Al-waisy!
Helpful article @gustavupp
I am very glad it was helpful Kumar!
One of the best articles so far. Was waiting for this.
Wow, you just made my day Bidisha!
I am glad it was helpful!
See you around.