Unique values in Swift: Removing duplicates from an array

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 do 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 and up with a quadratic time complexity o(N²). In short, this means that the more elements you have, the more the performance will decrees.

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 to true if the object was inserted and false 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!