## Set Theory

I find Sets and set theory to be a fascinating concept, both in the abstract and in the concrete , and one that I find more and more useful as time goes on. I’ve wanted to write an introductory post on the topic for a while now and hope it might inspire a similar interest in Sets for you.

### What is a Set?

A Set is a collection of unique elements. Those elements can be literally anything. There exists the set of all integers that are factors of 3, the set of all aquatic mammals, the set of all of *my* siblings.

A Set can be full of many elements, such as the set of all blades of grass in my lawn , or a set can be empty, such as the set of all humans living on planets other than Earth.

Sets, **in the abstract**, also don’t have a sense of “order”. If I’m talking about the set of all whole numbers between the values of `1`

and `5`

, they do not *necessarily* need to come in the order: `1, 2, 3, 4, 5`

. They can be thought of just as equally in the order: `2, 1, 4, 3, 5`

or any other order. In this way, Sets are like bag data structures.

Throughout the rest of this post, I will be using drawings to aid in my explanation, so to get started, here’s a Set:

It is important to note that Sets often exist within a universe:

### Sets interacting with other Sets

Sets become far more interesting (and useful) when we consider how they interact with one another.

### Unions

The union of Set A and Set B is the Set containing *all* the elements from Set A *and* all the elements from Set B.

```
function union(setA, setB) {
// Make a copy
const result = new Set(setA)
for (let element of setB) {
result.add(element)
}
return result
}
```

### Intersections

The intersection of Set A and Set B is the Set containing the elements in Set A *that are also* in Set B. You may recognize this as a Venn Diagram.

```
function intersection(setA, setB) {
const result = new Set()
for (let element of setA) {
if (setB.has(element)) {
result.add(element)
}
}
return result
}
```

### Differences

The difference of Set A and Set B is the Set containing all the elements from Set A that are *not* in Set B.

```
function difference(setA, setB) {
const result = new Set()
for (let element of setA) {
if (!setB.has(element)) {
result.add(element)
}
}
return result
}
```

Note that the `difference`

between two sets is dependent upon the order of the arguments. That is, given that Set A and Set B are not equal sets, then `difference(setA, setB)`

will not be equal to `difference(setB, setA)`

### Symmetric differences

The symmetric difference of Set A and Set B is the Set containing all the elements that are *not shared* by Set A and Set B.

```
function symmetricDifference(setA, setB) {
const result = new Set()
for (let element of setA) {
if (!setB.has(element)) {
result.add(element)
}
}
for (let element of setB) {
if (!setA.has(element)) {
result.add(element)
}
}
return result
}
```

Note, unlike `difference`

, `symmetricDifference`

will achieve the same result regardless of argument order. In this case, `symmetricDifference(setA, setB)`

will result in the same set as `symmetricDifference(setB, setA)`

.

### Complements

The complement of a Set A is the Set containing all of the elements *not* in Set A. By default, this would be all the other elements in the universe, but when constrained, complements can be useful.

For example, the symmetric difference of Set A and Set B is equivalent to the complement of the intersection of Set A and Set B. We can figure this out by first creating the union of Set A and Set B, then their intersection, and then getting the difference of those two results.

```
const all = union(setA, setB)
const shared = intersection(setA, setB)
const notShared = difference(all, shared)
```

### Subsets and Supersets

Set A is a subset of Set B if every element of Set A is in Set B.

We can make use of `Array.prototype.every`

to help us with this one.

```
function subset(setA, setB) {
return [...setA].every(element => setB.has(element))
}
```

Set A is a superset of Set B if Set B is a subset of Set A.

```
function superset(setA, setB) {
return [...setB].every(element => setA.has(element))
}
```

Subsets and supersets are useful when categorizing elements. For example, `React`

is a subset of `JavaScript`

. If someone tells me that they know `React`

, I can safely deduce that they know some `JavaScript`

as well. However, if someone tells me they know `JavaScript`

, I cannot deduce that they know some `React`

. They might, they might not.

### Disjointed Sets

Set A and Set B are considered disjointed if they share no elements.

Another way to think about disjointed sets is that their intersection is the empty set, and therefore has a `size`

of `0`

. We can use that to determine if two sets are disjointed or not.

```
function disjoint(setA, setB) {
return intersection(setA, setB).size === 0
}
```

It is not uncommon to create disjointed unions, as we will soon see.

### Where am I using Sets without maybe realizing it?

Type systems!

When you describe a `value`

as the type `string`

, what you’re really saying is that this `value`

belongs to the Set of all `string`

s. When you type an `array`

as `Array<number>`

, you’re saying that this `array`

belongs to the Set of all arrays containing numbers.

This is why types that use `|`

and `&`

are called union types and intersection types respectively.

If I have a type that is `string | number`

, I’m really saying that this item belongs to the union of the set of all strings and the set of all numbers. This is a disjointed union, since it’s impossible for a value to be both shared by the set of strings and the set of numbers.

### Are there practical uses?

Quite a few, but you have to get used to looking for them. I have used sets for tag/category systems (I use `Set`

s for my tags on this site). I’ve used them for making efficient filtering systems, too. It’s sometimes easiest to think of filtering items in terms of unions and intersections.

Another place you’ve probably seen them over and over is in vector drawing tools. Take a look at Figma for instance. If I make two vector shapes, select them both and then look at my options, don’t these look strangely familiar? They even use some of the same words to describe the operation.

At its core, manipulating vector graphics is as simple as doing some Set operations. Every shape is a Set of points, and we can use the operations to manipulate those points into complex shapes.

### Summary

Sets are a collection of unique elements. Learning the fundamentals of Sets and set theory may open up some new ways of thinking of operating with data for you. Your mileage may vary. At the very least, I hope you find words like “unions” and “intersections” less intimidating (if they ever were at all).

### Resources

There’s one specific library I like to share with people in regards to `Set`

s that I want to share with you: Zet. It’s incredibly simple and excellent. I encourage you to read the source code as well.

### Additional Notes

It’s worth noting that there are proposals to improve `Set`

s in JavaScript in the works: https://github.com/tc39/proposal-set-methods. The work here will replace the need for libraries like Zet in the future.