Introduction to JavaScript Sets
Tags: webdev, javascript, beginners, codenewbie
Published: 2020-01-28 02:15:34

**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 the `Set` and returns the `Set` with the new element inserted.
*   `clear`: Removes all elements from a `Set`.
*   `delele(value)`: Removes the given `value` from the `Set` and returns the `value` that the `has` function would have returned.
*   `entries()`: Returns a new iterator containing the `value` for each item in the `Set` object, sorted by their insertion order. Each entry has an `array` with the `key` and the `value`. The `key` will be set to the `value` of the `Set` entry.
*   `forEach(callbackFn, thisArg)`: Allows iteration through the entries of the `Set` and works with the data in the callback function that’s passed in. The value of `this` in the callback by passing in a variable for the `thisArg` parameter.
*   `keys()`: Gets the `keys` of the `Set` in a new iterator object that has the `value` of each element.
*   `values()`: Gets the `values` of each `Set`’s insertion order.
*   `Set.prototype[Symbol.iterator]`: Returns a new iterator object that has the `value` of each element in the `Set` in insertion order.

* * *

Defining and Manipulating Sets
==============================

To define a `Set` object, we write:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
set.has(1)
```

It will return `true`. If we call:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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.