With ES6 Map and Set where introduced into JavaScript. They finally bring more sophisticated data structures to your web applications. Because of that they are a very welcome addition to the language. For too long arrays have been forced to solve every single problem that is out there (however, I don’t see this one chancing anytime soon). But, in my opinion they really missed their mark with regards to the implementation.

Quick introduction to Map and Set

Lets start from the beginning: what are maps and sets? A map is a data structure that holds key-value pairs. In this regard it is a bit like an object, but there are some major differences:

keys aren’t limited to strings, numbers and symbols

map is a collection, meaning it has a size, an order and can be iterated over.

maps are designed to deal with optional keys instead of with required keys. This is more important when using TypeScript and ensuring type safety

Sets are collections where all value are guarantied to be unique. It is properly best described as a map where the keys and values are always the same. Sets are very good in checking if values exists and in calculation the difference or overlap between two collections.

Where it all went wrong

Based on the points above one would amuse that map and set are both an amazing addition to the language. And they would be, was it not for how Spartan their interface is. Lets start of a with Map. For this argument I will introduce an example use case of a map:

A Map holding a dynamic collection of counters

Lets say I want to increase the counter for a certain key and when the key isn’t preset it should become 1. With the ES6 map that would look somewhere around the lines of this:

😢 😢 😢

That is a lot of code when you realize that the entire point of a map is to deal with key-value pairs! For example, in immutable.js this would be written with a single call to update:

This is how easy key-value pairs should be when using a map

Note: I do love immutable.js, but are not saying you should use it over ‘normal’ maps and sets. Immutable and mutable data structures are a different concept and not just interchangeable.

Another example of a major miser is the lack of transformation methods on Map and Set. There is no map to transform every value in the collection and no filter to exclude values from the map or set. Lets say that we want to remove all counters that are zero. With the ES6 map we will need a for-loop with a new map for this:

An if-statement in a for-loop in the era of functional programming and lambda's

While with a simple filter method this could have been done with a single line and an arrow function. For me it is a mystery why arrays got all of those methods and they didn’t put them on sets and maps.

If it just would have been an array…

The last example I want to give about Map is the lack of a merge function. A common task with maps is to merge two maps together and resolving conflicting keys in certain way (normally override with the latter one or with a given custom merger). With the ES6 map this would again require a for-loop and some if-statement. One would expect to simply do this with map.merge(otherMap) .

All of the above points do also apply to Set, as it is a very comparable data structure to a map. They main additional miser here being the lack of a merge , except and both . Set are normally brilliant when writing code that cares about the overlap of difference between multiple sets of values, but not in JavaScript.

At last: remember that Map and Set are both ordered collections? There is no way of sorting them, except converting it to an array, sorting the array and recreating your map or set. Just why would you explicitly implement an ordered map, but don’t allow the user to order their map?

How to do it right

After all the complaining it is time to talk about how to do it right. For a start: be consistent in your interfaces when designing a standard library. If I can map, filter and reduce one type of collection in a certain way I would expect all the other ones to behave similar. Set, Map and Array should all have comparable interfaces. Ideally their would be an ICollection interface that forces all collection to be consistent (for both builtin and custom implementations).

Another point is to think about why those collections exist. If a collection soul purpose is to, lets say work with key-value pairs, I would expect there to be everything to create, delete and modify the values with their keys. When a Set is primarily there to deal with collections of unique values I would expect there to be everything to actually use this capability and not to have to implement standard library functions like merge and except . Especially in a high-level scripting language like JavaScript that is designed to be easy to use.

At the end of this article I have included a code snippet with the implementation of most of the mentioned features. Actually, most of them are really easy. It is just a pain that you have to think about those basic functionalities while your brain should be focused on the problem you where trying to solve in the first place.

Conclusion

What is your opinion on Map and Set in JavaScript? Do you use them a lot or are you like me and are still using third-party libraries for data structures? In my opinion the new ES6 data structures are just not good enough to be practical. I will sign this one of with an example implementation of most of the features I mentioned. Just to show that it shouldn’t have been a big deal to do it right the first time around.