ES6: Working with Sets in JavaScript

In this post we're continuing to look at features introduced in ES6. Amongst all the cool things happening in ES6, we find a few new data structures — one of them being the Set.

So what is actually a Set?

A Set is a data structure that store unique values of any type. That means you can mix in different primitive types as well as object references in the same Set.

Making a Set

Making a Set is simple — all you have to do is to initialise the object.

new Set();  
//> {}

That's it — we've made an empty set.

Making a set without any values doesn't really make any sense, so let's add a few values.

To add values you got two options.

You could either add values during the initialisation of the set by providing an array with values.

new Set(['Jane', 'John', 'Luke']);  
//> {"Jane", "John", "Luke"}

Another option is to add values using the add method that the set object provides.

new Set().add('Jane').add('John).add('Luke');  
//> {"Jane", "John", "Luke"}

The reason for us being able to chain the add methods this way, is because the add function returns the object it's modifying.

It's important to be aware that the add method doesn't return a new object in an immutable fashion, but is only returning a reference to the same object.

How a Set defines uniqueness

As mentioned, a set stores unique values. So, what is a unique value for a set in JavaScript?

The uniqueness of a value is tested using a strict equal — ===.

In many cases this test gives the expected result.

1 === 1 // true  
'Luke' === 'Luke' // true  

This means that if we add several of these values that we consider to be equal, we're still only getting one of them stored in our set.

new Set([1,1,'Luke','Luke']);  
//> {1, "Luke"}

However, if we're taking a look at objects, we need to be a bit more careful.

The thing is, === will only return true if the objects it's testing has the same object reference.

const name = {name: 'luke'};  
name === name // true  

This means that two objects that seem identical, would be considered unequal if they have different references.

{name: 'luke'} === {name: 'luke'} // false

So, if we create three identical objects and add them to a set, they'll all be there because they're considered unequal.

new Set([{name: 'luke'}, {name: 'luke'}, {name: 'luke'}]);  
//> {{name: 'luke'}, {name: 'luke'}, {name: 'luke'}}

Interacting with a Set

Since a set is an object, we got a lot of different methods to help us interact with it.

Checking if the Set contains a certain element

One method — has — is enabling us to check if the set contains a certain element.

const names = new Set(['Jane', 'John', 'Luke']);

//> true
//> false

Removing elements from a Set

Another thing you can do with a set is to delete elements from it.

const names = new Set(['Jane', 'John', 'Luke']);


//> {"Jane", "John"}

If removing one element isn't enough, you could clear the whole set by using clear.

const names = new Set(['Jane', 'John', 'Luke']);


//> {}

Iterating a Set

Iterating a set can easily be done in one of two ways.

Either you could use a for loop.

for (let name of names) {  

or you could use the forEach method.

name.forEach(name => console.log(name))  

Using filter, map and reduce

When first seeing the forEach I was straight away thinking — "Cool, maybe it support filter, map and reduce as well?". The answer to this is sadly no.

If we want to use one of these methods, we first need to convert the set into an array.

Luckily this is quite easy using the spread operator.

[...names] // the set is now an array

Now we can start using the filter, map and reduce on our collection.

To show this, let's map each name in the set into an object.

[...names].map(name => {firstname: name});

Now, if we in addition want to convert the array back to a set, we need to initialise a new set with the array as a parameter.

new Set([...names].map(name => {firstname: name}));