# An introduction to the ensemble data structure in Javascript | by before the semicolon | April 2021

There are many situations where you need to compare multiple lists and extract items that they have or not have in common, available in one, etc. Sets let you do just that and more. In particular, the Javascript Set is very special and powerful, but it still lacks important elements that other languages offer.

Video version of this articleThis article is an improved and more detailed version of the article Define the series of data structures on Youtube that you can check if you prefer the videos.

## What is a set?

The set is a collection of unique items stored in no particular order. Unlike other types of collection such as Stack, Queue, and Array, a Set is used in comparing lists and for testing whether an item exists in a set or not. It is correct to say that a set stores key-value pairs because you are using the element to test if it is part of the set.

Set is also an abstract data type, which means that it is defined by its behavior just like stack and queue data structures. Due to its key-to-key nature, a set is more closely related to a map than anything else, and you can even implement a set using it.

## Javascript set

The JavaScript package is very basic and straightforward. It does not have common set operation capabilities that other languages typically offer. It also uses a unique algorithm to test if the items are the same against the strict triple equal check (===).

This means that to store “*indefinite*“,”*no*“and”*Nope*“Overall will ensure that they only exist once even if” Nah! == NaN ”. Where it shines is in storing object types that are difficult to verify equality.

const set = new Set([null, undefined, null, NaN, NaN, null]);console.log([...set]); //[null, undefined, NaN]

You insert with “*add*“And delete with the”*wipe off*“For a single element or the complete set with the”*clear*“method. It is iterable and comes with iterators for values and starters since the keys and values are the same. Hence the fact that it is often described as a set of key-key pairs. It also exhibits the “*for each*”Like Array and Map as a faster way to iterate its elements.

const set = new Set([78, 20, 44]);set.add(12);

set.delete(78);

set.clear();

As mentioned earlier, Set is used to do item comparisons and checks, but the only method that allows you to check something is the “*possesses*”Which, given an item, will return true or false, whether it is in the set or not.

`set.`**has**(12); // true

set.**has**(21); // false

To do more complex checks, you need to add new methods that can let you make powerful comparisons and work with list data nicely which I will show you later.

## Set vs Array

Set is very different from Array and the reason they are often compared to each other in JavaScript is that Array was often used to do things that Set does.

Arrays store items in index order and allow items to be read and written quickly as long as you know their index. Set does the same as long as you have the object. This shows that Array is a collection of indexed items and Set is a collection of key-based items.

The table is intended to be used when you want to keep the items in a certain order for access and manipulation. In a set, the elements are unique but can be repeated within an array. Set is often used to clear repetition of array elements, but it’s not meant to be used for things Array is intended for.

const array = [23, 41, 12, 41, 67, 23];constnoRepeatArray= Array.from(new Set(array));// becomes [23, 41, 12, 67]learn more about Array

Set does not and is not intended to replace the Array module. It just addresses a very specific problem of comparing lists and checking items that Array is not good at without extensive additional code to handle.

const array = [12, 45];

const set = new Set([12, 45]);// extra code needed to ensure uniquenessif(!array.includes(12)) {

array.push(12);

}// does nothing since it already existsset.add(12);// slower: checks every item if necessary

array.includes(45);// faster: since it checks the key

set.has(45);

## When to use Set?

You use Set when all you want is to perform comparisons and checks on a specific list. Suppose you have lists A and B and you want to know if they are the same. Which list A has that B doesn’t have or what elements do they have in common? In addition, the elements of the set are unique and this is an excellent property to operate.

You use it to keep a list of unique items to iterate later and do things so you don’t have to worry about the list of repeated items. So you use it when you have another sample list, you can find out how they differ or match to make certain decisions. You use it so that you can later change it to an array to do more array-like operations and vice versa.

## Define operations

In mathematics, whenever you talk about sets, you can perform operations. In fact, Set is a computational implementation of the mathematical finite set.

To properly show you the Set operations in code, extend the JavaScript set to inherit its property and methods and give it additional methods. For the code below, we only need one method that checks if a valid set is provided. For this example, a valid set must be an instance of the Set object and not empty.

`class `**SetExtended** extends **Set** {

#**isValidSet** = (set) => {

return set && set instanceof Set && set.size > 0;

};

}

**union**:

The union operation merges multiple sets and returns the result of the merge. For the implementation of the code below, we return a new set by dividing the current and given set into an array to create it.

union(set) {

if (!this.#isValidSet(set)) return new SetExtended(); return new SetExtended([...this, ...set]);

}

**Intersection**:

The intercept operation provides us with a new set containing only the things they have in common. The example below goes through the smaller set (avoids unnecessary checks) and checks if the element exists in the larger one and adds it to the intersection set and then returns it at the end.

intersection(set) {

const intersectionSet = new SetExtended();if (!this.#isValidSet(set)) return intersectionSet;const [smallerSet, biggerSet] = set.size <= this.size

? [set, this]

: [this, set];smallerSet.forEach((item) => {

if (biggerSet.has(item))intersectionSet.add(item);

});return intersectionSet;

}

**Difference**:

The difference operation returns a new set containing only the items that one set does not have in common with the other.**It is also known as subtraction.**The implementation below goes through the current set and pulls together the things that do not exist in the other into the differences set.

difference(set) {if (!this.#isValidSet(set)) return new SetExtended();constdifferenceSet= new SetExtended(); this.forEach((item) => {

if (!set.has(item)) differenceSet.add(item);

});return differenceSet;

}

**Intersection difference**:

The intersection difference operation is the opposite of the intersection.**It is also known by the exclusive name or**. It returns a new set containing all of the elements that they don’t both have in common and in the implementation below we just create a new set with their differences.

intersectionDifference(set) {

if (!this.#isValidSet(set)) return new SetExtended(); return new SetExtended([...this.difference(set),...set.difference(this),

]);

}

**Sub-assembly**:

A set is said to be a subset when all of its elements are contained within another. The below implementation first checks the size because one set cannot be a subset of another if it is larger, and then for each item it checks if it exists in the other.

isSubsetOf(set) {

if (!this.#isValidSet(set)) return false; returnthis.size <= set.size&&

[...this].every(item => set.has(item))

}

**Superset**:

A superset is the opposite of a subset. A set is a superset when it contains all the elements of another set of less than or equal size.

isSupersetOf(set) {

if (!this.#isValidSet(set)) return false; returnthis.size >= set.size&&

[...set].every(item => this.has(item))

}

You are not limited to these operations and are free to include more that addresses whatever type of need you may have. In general, these checks are extremely simple to implement, and you can still take advantage of the fact that Set and Array can be converted back and forth to take advantage of the powerful array methods.

**Source code:** *Check this out **full code on Github*

## Static set

A static set is a set that always contains the elements with which it was initialized. You cannot add, remove or delete its items. The JavaScript set is not static and will always expose methods that modify the set after it is created. To get a static set, we need to override this behavior the same way we extended it.

The easiest way is to extend it and replace the methods that modify it. That way you can’t change it later.

`class `**StaticSet** extends **SetExtended** {

constructor(items) {

super(items);**this.add = undefined;**

this.delete = undefined;

this.clear = undefined;

}

}

## Conclusion

In general, if you often find yourself checking for the existence of items in lists and filtering items for a certain part of the list, this is a clear sign that you need to explore sets. The JavaScript package is super powerful and easy to use and probably super underrated.

Check out the Before Semicolon blog for more data structure articles like this one. Also see the article on table data structure for more details.

*Youtube channel**: **Before the semicolon**Website**: **beforesemicolon.com*