TypeScript Set VS Array
I recently discovered the Set Object and want to write a little about it in this blog. The Set Object is a collection of unique values. The value type can be a primitive data type or an object of your choice (ECMAScript Language Types). Thanks to TypeScript a Set is a strongly typed collection of values which can only occure once.
Creating a set
A set can be created by using the Set`s constructor.
const mySet: Set<number> = new Set([1, 2, 3]);
console.log(mySet)
[LOG]: Set (3) {1, 2, 3}
It is also possible to use the add() function to add values to a Set.
const mySet: Set<number> = new Set();
mySet.add(1)
mySet.add(2)
mySet.add(3)
console.log(mySet)
[LOG]: Set (3) {1, 2, 3}
As already mentioned a Set will only accept unique values. If you try to add a duplicated value, the Set will simply ignore it.
const mySet: Set<number> = new Set([1, 2, 3, 3, 2, 2, 1]);
console.log(mySet)
[LOG]: Set (3) {1, 2, 3}
Incidentally, this property is one of the more interesting ones, because it allows you to elegantly remove duplicate entries from a list. Before you can understand the next example, you need to understand that it is very easy to turn an array into a set and a set into an array. So let’s assume that there is an array with several duplicate entries that you want to remove. One way to do this could be to convert it into a set.
const dirtyArray: string[] = ["A", "B", "C", "C", "D", "E", "F", "F", "F", "G", "G"]
const cleanSet: Set<string> = new Set(dirtyArray)
console.log(cleanSet)
[LOG]: Set (7) {"A", "B", "C", "D", "E", "F", "G"}
And for the sake of completion let`s make it an array again by using the Array.from() function.
const dirtyArray: string[] = ["A", "B", "C", "C", "D", "E", "F", "F", "F", "G", "G"]
const cleanSet: Set<string> = new Set(dirtyArray)
const cleanArray: string[] = Array.from(cleanSet)
console.log(cleanArray)
[LOG]: ["A", "B", "C", "D", "E", "F", "G"]
Looking for elements and deleting them
In order to check an element exists inside a Set the has() functionality can be used.
const mySet: Set<number> = new Set();
mySet.add(1)
mySet.add(2)
mySet.add(3)
const match = mySet.has(2)
const mismatch = mySet.has(5)
console.log(match)
console.log(mismatch)
[LOG]: true
[LOG]: false
The Set object offers deleting and clearing functions in addition.
const mySet: Set<number> = new Set();
mySet.add(1)
mySet.add(2)
mySet.add(3)
mySet.delete(2)
console.log(mySet)
[LOG]: Set (2) {1, 3}
mySet.clear()
console.log(mySet)
[LOG]: Set (0) {}
You can iterate over a Set by using its forEach() or values() functions.
const mySet: Set<number> = new Set();
mySet.add(1)
mySet.add(2)
mySet.add(3)
mySet.forEach(n => console.log(n))
[LOG]: 1
[LOG]: 2
[LOG]: 3
const values = mySet.values()
for(let n of match) {
console.log(n)
}
[LOG]: 1
[LOG]: 2
[LOG]: 3
Sets can be compared with each other to find differences or similarities. However, they can also be easily combined.
const myCars = new Set<string>(["Audi", "VW", "Opel"]);
const friendsCars = new Set<string>(["Tesla", "Mercedes", "Opel"]);
const combined = new Set<string>([...myCars, ...friendsCars]);
console.log(combined);
[LOG]: Set (5) {"Audi", "VW", "Opel", "Tesla", "Mercedes"}
const similarities = new Set<string>([...myCars].filter(x => friendsCars.has(x)));
console.log(similarities);
[LOG]: Set (1) {"Opel"}
const differences = new Set<string>([...myCars].filter(x => !friendsCars.has(x)));
console.log(differences);
[LOG]: Set (2) {"Audi", "VW"}
Why and when you should use sets
There are arrays that can do everything that sets can do.
So why should you include a set in your everyday toolbox? There are surprisingly good reasons for this.
Uniqueness
A Set automatically ensures that all its elements are unique. This can be extremely helpful in your day to day development.
But beware that a Set will simply ignore duplicates. It will not throw an error if you add a value which already exists,
it will simply be not part of the collection.
Time complexity
To perform a membership check, a list must be run through. In the worst case, an array has a time complexity of O(n). This means that the more elements an array contains, the longer the membership check takes. A set, on the other hand, has an average time complexity of O(1). This is because a set is based on a hashmap. As a result, an average complexity of O(1) is normally achieved for the functions (add, has, delete). However, collisions in the hashmap can result in sets also having a complexity of O(n). If multiple values hash to the same bucket, the Set degenerates into a linear search for that bucket. With the latest browser engines and hashtable implementations, however, this should not be a problem.
All of this makes Sets usually better performing than Arrays, which is not really recognizable in smaller lists.
Built-in Methods
As already depicted above Sets have some cool and simple methods making it pleasent to use.
- Add (add)
- Remove (delete)
- Check existence (has)
I personally like this more than manually handling an array with filter() and find()
For Finding Differences or Similarities
If you have two lists and want to find out what they have in common and what they don’t using a set is super quick and easy, as I’ve shown in the example above.
Why and when you should NOT use sets
Do not use sets if you need to access elements via an index. This is not supported by sets.
Even if you need [key, value] pairs, you should not use them, but use a map instead.
If you need duplicates in your list, sets are (of course) not the right solution.
You want to sort your list? Unfortunately, sets are sorted by insertion and do not have a
sorting method and you cannot simply move items in the list without creating a new set.
So do not use sets if you want to sort dynamically. Or make sure that you add already pre-sorted elements to your set.
Summary
Sets are a good and quick alternative to arrays. Sometimes.
Do you need unique, unordered data with efficient membership checks and operations? Use sets.
None of this is important to you? We still have Map, Array, Object
Have fun coding ;)