Subscribe to my email list now at http://jauyeung.net/subscribe/
Follow me on Twitter at https://twitter.com/AuMayeung
Many more articles at https://medium.com/@hohanga
Even more articles at http://thewebdev.info/
In JavaScript, Set
objects are objects that hold collections of values. They can be iterated in the order they’re inserted. Each value in a Set
may occur only once. This means that a Set
in JavaScript is consistent with sets in math.
Because each value in a Set
has to be unique, their values will be checked for equality. This is done by using the triple-equals operator for comparison; meaning -0 and +0 are different values.
However, NaN
is considered to be equal to itself, even though in other parts of JavaScript it’s not. undefined
can also be stored in Set
s.
Each Set
can be created with a constructor, and has the size
property to get the number of items in each Set
. Set
s can be manipulated via many methods.
These include:
-
add
: Adds a new element to theSet
and returns theSet
with the new element inserted. -
clear
: Removes all elements from aSet
. -
delele(value)
: Removes the givenvalue
from theSet
and returns thevalue
that thehas
function would have returned. -
entries()
: Returns a new iterator containing thevalue
for each item in theSet
object, sorted by their insertion order. Each entry has anarray
with thekey
and thevalue
. Thekey
will be set to thevalue
of theSet
entry. -
forEach(callbackFn, thisArg)
: Allows iteration through the entries of theSet
and works with the data in the callback function that’s passed in. The value ofthis
in the callback by passing in a variable for thethisArg
parameter. -
keys()
: Gets thekeys
of theSet
in a new iterator object that has thevalue
of each element. -
values()
: Gets thevalues
of eachSet
’s insertion order. -
Set.prototype[Symbol.iterator]
: Returns a new iterator object that has thevalue
of each element in theSet
in insertion order.
Defining and Manipulating Sets
To define a Set
object, we write:
let set = new Set();
set.add(1);
set.add(5);
set.add(5);
After the three calls to the add
function, we should get [1,5]
when we log the value of the Set
variable, because we can’t have duplicates in a Set
object.
This only applies to primitive types. So, if we insert two objects with the same content, both will be in the Set.
Only the reference is checked when inserting items into a Set
, consistent with the triple-equals check.
For example, if we write:
let set = new Set();
for (let i = 0; i < 5; i++){
set.add({a: 1, b: 2});
}
Then, we’ll get {a: 1, b: 2}
in the Set
five times, because each object does not have the same reference in memory.
We can check if an element exists in a Set
with the has
function. For example, if we have:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);
Then if we call the has
function:
set.has(1)
It will return true
. If we call:
set.has(10)
then it will return false
, since the element doesn’t exist in the Set
. We can also put expressions into a Set
function. If we call has
as in the following code:
set.has(Math.sqrt(25));
We still get true
, because the expression is computed before it’s passed into the has
function’s parameter, so it’s the same as set.has(5)
.
To get the size
of the Set
, we can use the size
property as in the following code:
set.size
Which would be 8
, since there are eight elements in the Set
.
To remove an item from the Set
, we can use the delete
function as in the following code:
set.delete(8)
Then 8
would removed from the set. If we call set.has(8)
, it’ll be false
, because 8
is no longer in the Set
.
To iterate through the value of a set
, we can use the for...of
loop. In the following example, we will loop through the entries:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);
for (let item of set){
console.log(item)
}
In the loop above, we get 1
to 8
logged in the order that they were inserted with add
.
To loop through the keys
of the Set
, we can use the keys()
method to get the key
s from the Set
object. For example, we can write the following code:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);for (let key of set.keys()){
console.log(key)
}
In the case of Set
objects, the key
would be the same as the value, so that we would still get 1
to 8
logged in the console when we run the code above. To loop through the values, we can call the values
method on the Set
object as in the following code:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);for (let value of set.values()){
console.log(value)
}
As we can see, we got the same values as we did from the keys
method. This is because with Set
s, keys
and values
are always the same. To get the keys
and values
all at once, we can use the entries
method along with the destructuring assignment operator to decompose the key
and value
into their own variables in each entry, as in the following code:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);for (let \[key, value\] of set.entries()){
console.log(key, value)
}
We can see that the key
and value
of each entry of a Set
is the same, as we expected.
Converting Sets Into Arrays
To convert a Set
into an array
, we can use the Array.from
method. We pass in the Set
as an argument of the from
method. For example, we can write:
let set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(7);
set.add(8);
const arr = Array.from(set);
console.log(arr)
From running the code above, we see that arr
is an array
with the same elements in the same order as the Set
. This is handy for removing duplicate elements from an array
. To remove duplicates from an array
, we can convert it to a Set
by using the Set
’s constructor and then convert it back to an array
with Array.from
. We can write the following:
let arr = [1,1,1,1,1,2,2,2,3,4,5,5,6,7,8];
const set = new Set(arr);
arr = Array.from(set);
console.log(arr);
When we log
arr
in the last line of the code above, we get [1, 2, 3, 4, 5, 6, 7, 8]
. This is because we passed arr
into the Set
’s constructor, which created the Set
, where the duplicate values are automatically eliminated. Then we call the Array.from
function with set
as the argument, so we convert it back to an array
. The Array.from
function returns an array
, so we can assign it back to arr
and get back an array
with the duplicates removed.
An alternative way to convert a Set
into an array
is to use the spread operator. For example, we can rewrite the example above by using the spread operator:
let arr = [1,1,1,1,1,2,2,2,3,4,5,5,6,7,8];
const set = new Set(arr);
arr = [...set];
console.log(arr);
We get exactly the same result. This is a bit shorter and does the same thing as the Array.from
function since the spread operator will make a copy of the object that it’s spreading.
Doing Mathematical Set Operations on JavaScript Sets
Since we can convert between arrays
and Set
s, we can apply array
operations to them. Set
s do not come with any methods for doing things like getting the intersection, difference or union with other Set
s. However, since we can convert between array
s and Set
s, we can use array
functions to do the same thing. For example, if we want to get the intersection between two Set
s, we can write:
let set1 = new Set();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);let set2 = new Set();
set2.add(1);
set2.add(2);
set2.add(7);
set2.add(8);const unionBetweenSet1and2 = new Set([...set1,...set2])
console.log(unionBetweenSet1and2);
When we log the intersectionBetweenSet1and2
, we get 1
and 2
in the Set
, which is what we expect, since 1
and 2
are the only elements that are in both Set
s. In the Set
constructor, we converted set1
into an array
with the spread operator and then called filter
on it to only include the items in set1
that are in set2
with the has
method called on set2
.
To get the set union of two Set
s, we just call the spread operator on both, as in the following code:
let set1 = new Set();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);let set2 = new Set();
set2.add(1);
set2.add(2);
set2.add(7);
set2.add(8);const differenceBetweenSet1and2 = new Set([...set1].filter(item => !set2.has(item)))
console.log(differenceBetweenSet1and2);
When running the code above, we should see that unionBetweenSet1and2
has the value [1,2,3,4,7,8]
.Since we passed both Set
s into the array
and spread them with the spread operator, we created an array
with all the elements in the array
. Then the set constructor eliminated the duplicates, and we’re left with elements in both set
s, which is the union of both set
s.
If we want to get the set difference between two Set
s, we can convert one Set
into an array
and then use the filter
function on it to exclude the elements from the other Set
. For example, we can write:
let set1 = new Set();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);let set2 = new Set();
set2.add(1);
set2.add(2);
set2.add(7);
set2.add(8);const differenceBetweenSet1and2 = new Set([...set1].filter(item => !set2.has(item)))
console.log(differenceBetweenSet1and2);
When we log the value of differenceBetweenSet1and2
, we should get[3,4]
because we converted set1
into an array
with the spread operator and called the filter
on it. In the callback function of filter
we returned !set2.has(item)
to exclude the elements from set2
that are also in set1
, so we’re left with the elements that are only in set1
.
To get the symmetric difference between two Set
s, i.e., the set of elements that are only in one Set
or the other, we can get the intersection between two Set
s and then exclude the elements that are in the intersection. For example, we can write the following:
let set1 = new Set();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);let set2 = new Set();
set2.add(1);
set2.add(2);
set2.add(7);
set2.add(8);const intersectionBetweenSet1and2 = new Set([...set1].filter(item => set2.has(item)))const symDiffBetweenSet1and2 = new Set([...set1,...set2].filter(item => !intersectionBetweenSet1and2.has(item)))
console.log(symDiffBetweenSet1and2);
In the code above, we obtained the intersection between set1
and set2
. Once we got intersectionBetweenSet1and2
, we put everything in set1
and set2
in the same array
by using the spread operator, then called filter
on that array and returned !intersectionBetweenSet1and2.has(item)
in the callback function to exclude the elements that are in the intersection. This is what we want to eliminate from the array
to get the symmetric difference. Then, we just convert the result back to a Set
with the Set
constructor. At the end, when we log symDiffBetweenSet1and2
, we should get [3,4,7,8]
which are elements that are only in one Set
or the other.
To check if an element is a Superset
of another set
, we can once again convert it to an array
and use that to check. For example, if we have:
let superSet = new Set();
superSet.add(1);
superSet.add(2);
superSet.add(3);
superSet.add(4);let set = new Set();
set.add(1);
set.add(2);const isSuperset = [...set].every(item => superSet.has(item));
console.log(isSuperset);
In the code above, we convert set
to an array
and then check if every element of set
is included in the Superset
with the has
function. The every
method is an array method that checks if every element in an array
meets a specified condition.
We can also create Set
objects from strings. We can pass a string straight into the Set
constructor. The result would be a Set
that has the individual characters of the string with the duplicate entries removed. For example, if we have the following code:
const letterSet = new Set('element');
Then we get back {“e”, “l”, “m”, “n”, “t”}
when we run console.log(letterSet)
. Note that if there are duplicates, the characters that are inserted later which are the same as any existing characters will be omitted, so the one that is inserted earlier stays.
JavaScript Sets
is a useful data structure that models the properties of mathematical sets. Set
s can be converted into array
s, so we can manipulate them as array
s and then turn them back into Set
s. This is very handy for working on them, since Set
s do not have as many methods as array
s have.