Removing duplicates to get unique values out of an array can be a common task to perform. Languages like Ruby have built-in methods like uniq
but in Swift, we have to create such methods on our own. The standard library does not provide an easy method to do this.
There’s a lot of ways to achieve the same result and each of them has their own pros and cons. Let’s go over them and see which methods work best for your use-case.
Using a Set to remove duplicates by default
Before we start diving into extensions to remove the duplicates from an array it’s good to look into Sets in Swift. A Set
by default contains only unique values and can achieve the results you’re aiming for.
Go for a Set
if:
- Order is not important
- You like to have unique elements by default
let array: [Int] = [1, 1, 1, 2, 2, 2, 3, 3, 3]
let set: Set<Int> = [1, 1, 1, 2, 2, 2, 3, 3, 3]
print(array) // prints: [1, 1, 1, 2, 2, 2, 3, 3, 3]
print(set) // prints: [2, 3, 1]
This is more a code design decision where you decide to work with a Set
versus an array. Sets have the benefit of being more performant as well which makes them a good candidate to solve the uniqueness problem. You can read more about the differences between a Set
and an Array
in my blog post Array vs Set: Fundamentals in Swift explained.
Without going too much into detail on the differences it’s good to know that a Set
is not maintaining the order. If keeping the order is important in your case you might not want to go with a Set
. You could go for an NSOrderedSet
but this class does not give you type completion and you’ll have to work with Any
instances.
Removing duplicate elements from an array with an extension
When the order of the elements is important we can continue working with an array and get the unique values by making use of a custom extension.
Go for an Array
if:
- Order is important
- You can’t easily switch over to a
Set
We have to create an extension to allow us to filter out duplicate elements. It’s important to keep performance in mind when creating that extension as we could easily end up with a quadratic time complexity o(N²)
. In short, this means that the more elements you have, the more the performance will decrease.
The following piece of code relies on the Hashable
protocol to match elements and has a linear time complexity of o(N)
. This means that 3 elements require 3 iterations, 10 elements require 10 iterations, and so on.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: Set<Iterator.Element> = []
return filter { seen.insert($0).inserted }
}
}
print(array.unique()) // prints: [1, 2, 3]
Let’s break down this unique()
method:
- We create a
Set
to track seen objects - A filter is used to iterate over all objects
- the
insert(_:)
method returns a tuple including an inserted boolean which is set totrue
if the object was inserted andfalse
if not - The inserted boolean value is used to filter out duplicates from our array
The final result is an array with the same order but without duplicate elements. The only downside is that your elements need to conform to the Hashable
protocol but this shouldn’t be that big of a problem. In fact, many types in the standard library already conform to the protocol, like Strings, integers, and Boolean values. The Hashable
protocol is used to determine whether an element is equal to an existing object and therefore required to get the unique values.
Conclusion
When you like to get the unique elements of a collection you can go for a Set
or an extension on an Array
.
- If order is not important, go for a
Set
- Keep the order and fetch the unique elements with an extension on an array
If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.
Thanks!