ES6 Sets are awesome

I came across Sets some time ago is starting to like it. I also caught myself needing it more than just in one occasion. In this article I will try to cover why I think Sets are awesome and how to make use of them.

I will cover:

  • What a Set is and how to create one
  • Finding specific elements with Sets
  • Get the size of a Set
  • Filter out duplicate values from an Array with a Set
  • Looping
  • Performance of Sets vs Arrays

What is a Set

Sets are a new object type which you can use in ES6. Sets allows you create collections of unique values and are also iterable like arrays. A set can hold values like simple primitives, strings, or it can hold objects literals or arrays

The official description of Sets reads: 
 
Set objects are collections of values. You can iterate through the elements of a Set in insertion order. A value in the Set may only occur once; it is unique in the Set's collection

How to create a Set

Creating a Setis quite straightforward. We simply create a newinstance of a Set like so:

let mySet = new Set();

Simple right? Making a Set without values doesn’t really make sense. You can easily add values to a Setusing a few different approaches. Either by adding an iterable object as a parameter or by using the add() method located on a Sets prototype.

//Adding data from array
let mySet = new Set(someArray)
//Add two simple values
let mySet = new Set();
mySet.add(‘Donatello’).add(‘Leonardo’);

A Setcan only store unique values, which means if you were to add “Leonardo” twice, it will only hold one value for “Leonardo”

Sets holds other methods like

  • has()
  • size()
  • delete()
  • size()
  • etc….

You can learn more about this by taking a look the docs: 
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Methods

As mentioned, Setsallows you to create collections of unique values, so what does actually this mean?
Basically it means that values in a Set is tested for it’s equality, much like you would do when using the === (equality operator in javascript).

100 === 100 // true  
'Leonardo' === ''Leonardo' // true
100 === ‘100’ // false

Finding a specific element

To check an element exists in a Set, we have to make use of the has()method. This will return a true/false value, much like the array.prototype.includes()method.

console.log(mySet.has(1)); //true
console.log(myArray.includes(1)); //true
console.log(myArray.indexOf(1)); //0

However, Sets doesn’t support getting a specific value, like an array does, hence it will return “undefined” if you try:

console.log(mySet[0]); //undefined
console.log(myArray[0]); //1
Personally I find the Set.has() more simple to use than array.indexOf()
Also note that if you find yourself to be using array.includes(), it’s not supported in Internet Explorer

Get the size of a Set

When we have a unique Set, we often need to get the size of a size, much like when you do array.lengthwhen dealing with arrays.
Setholds a method that allows us to get the size very easily, called size()… (Go figure ;) ) 
The size method returns the number of elements in a Setobject.

const number_of_different_companies = new Set(arrayOfData.map(item => item.getCompany().id)).size; // Will return a number, ex: 10

Filter out duplicates in arrays

When using Sets, it’s possible to pass an array. This will create a unique set of values from your array, which means it will remove any duplicate values from your array.

Let’s take a closer look.

// Create array
const myArray = ['Splinter', 'Rafael', 'Donatello', 'Michelangelo', 'Shredder', 'Splinter'];
//Convert to unique set of values
const myUniqueSet = new Set(myArray); // returns: "Splinter", "Rafael", "Donatello", "Michelangelo", "Shredder"

// Convert back to array
const myUniqueArray = [...myUniqueSet]; //returns "Splinter", "Rafael", "Donatello", "Michelangelo", "Shredder"

First thing we are doing is creating an array with our values, then passing it onto a Set, which removes all duplicate values from the array (The Splinter value that we have twice in the array). 
Last thing we are doing is converting it back into an array. The main reason for this is that Setsdoesn’t support array methods like:

  • array.map()
  • array.reduce()
  • array.filter()
  • etc…
Be aware it can be tricky when you are working with objects, as the equality will only return true if the objects the Set is testing has the same object reference. 
I personally had issues where I had 2 objects that seemed identical, but wasn’t considered to be equal, as they had different object references.

Learn more about object references in this article: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

Looping Sets

Sets does however hold a forEach method on its prototype, which means we can loop all values in a set without having to convert it back to an array. 
It’s also possible to loop all values using a for of … loop

//ForEach
myUniqueSet.forEach(value => console.log(value)); // Logs each value

//For of
for (let value of myUniqueSet) {
console.log(value) // Logs each value
}

You can learn more about the available methods you use here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Methods

Sets are fast, very fast.

If you have been in the javascript world for some time and have worked with data before, you’ve probably also came across arrays at some point. 
In arrays we can add/remove data, find specific elements etc…

  • array.push()
  • array.splice()
  • array.indexOf()

You can do the same with Sets by using some of the prototype methods you have available.

Here you will find methods like:

  • set.add()
  • set.remove()
  • array.has()

You can learn more about this in this article: https://alligator.io/js/sets-introduction


After hearing and reading how Setscan improve speed in your javascript application I got intrigued do to some tests of my own. To my surprise, Setsare not just only fast, they are insanely fast compared to arrays.

Today, browsers are generally pretty fast, and they are also very fast to parse and execute our javascript code, so we need to create an array with enough items that will give us some sort of latency so we can measure performance between our operations.

I tried comparing the methods mentioned above on an arraysize of 50.000 items. 
To measure the performance, we will use the browsers own console, using console.time

Start off by creating an array with a default of 50.000 items:

NOTE: Note that fill() will only work in browsers with ES6 support.

const arraySize = 50000;
let array = new Array(arraySize).fill(arraySize);
console.log( array.length ); // 50000

Compare Array.push and Set.add()

Let’s try and compare the above methods and see how they perform. 
In this example we are adding 50001 to the existing arrayand Set
 
arrayTest: 0.36181640625ms
setTest: 0.010009765625ms

//Add values
console.time( 'arrayTest' );
array.push( arraySize + 1 );
console.timeEnd( 'arrayTest' );
let set = new Set( array );
console.time( 'setTest' );
set.add( arraySize + 1 );
console.timeEnd( 'setTest' );
console.log( "array length", array.length ); // 50001
console.log( "setsize", set.size ); // 500001*/

As you can see, Sets is way faster — Actually 189% faster.

Compare Array.splice and Set.delete()

Let’s try removing 40.000 items from the array and set and see how that compares in performance:

arrayDelete: 0.147216796875ms
setDelete: 0.011962890625ms

/Delete values
let deleteFromArray = ( arr, item ) => {
var i = array.indexOf( item );
i !== -1 && array.splice( i, 1 );
};
console.time( 'arrayDelete' );
deleteFromArray( array, 40000 );
console.timeEnd( 'arrayDelete' );
let set = new Set( array );
console.time( 'setDelete' );
set.delete( 40000 );
console.timeEnd( 'setDelete' );

Again, Sets are performing way better. This time 169% better.

Find a specific item the collection

Lets try finding a specific item — We can do this by using array.indexOf() and set.has()

arrayFind: 0.1728515625ms
setFind: 0.0859375ms

// Helpers
let checkArr = ( array, item ) => array.indexOf( item ) !== -1;
let checkSet = ( set, item ) => set.has( item );
// set variables
let set, result;
console.time( 'arrayFind' );
result = checkArr( array, 40000 );
console.timeEnd( 'arrayFind' );
set = new Set( array );
console.time( 'setFind' );
checkSet( set, 40000 );
console.timeEnd( 'setFind' );

Once again, Sets are proving itself way faster, a 67% increase in performance.

Your result may show some other numbers. The results can vary from device, OS, browser etc.
I did my tests on a laptop with Win10, and Chrome73
See my test on Codepen and Fork it if you like to do your own test: https://codepen.io/NickyCDK/pen/bJOWBj

Final thoughts

I’m no expert in using Sets, but after what I’ve seen it seems pretty da** awesome.

It seems that Setsgenerally perform way better than arrays. Does this mean you shouldn’t use arrays anymore? 
I would say no. 
It is not a replacement for arrays, as there a lot, arrays can do, that Sets can’t and vice versa. For example, in Sets you can’t access a specific index, and get the value of that index.

The biggest differences are that arrayscan hold duplicates of values and Setscannot (No matter what you do)
In addition, Arrays are indexed collections, and Sets are keyed collections. 
 
The performance improvements are surprising to me, and personally I will probably be using Setmore frequently, as it seems they are performing way better when locating elements and when adding/removing elements compared to arrays.
Also, if you remember… on sets we can’t use the array methods. This can however be solved by converting a Setback into an array:

// Example on convert set to an array and using the array.map() method.
let myArray = […mySetOfNames].map(name => {firstname: name.firstname});

When you convert a Set back into an array, you have all array methods available again as it is now an array.


In general, to my opinion a combination of the two seems like the way to go to depending on scenarios, but I would love to hear your opinion and how you would use it and what you are using it for?

If you’d like to catch up with me sometimes, follow me on Twitter | Facebook or simply visit my portfolio website (That is however in Danish)