105

I'm building an iOS app with swift and i need to get all unique values of array of strings.

I've been reading the apple developer docs but it doesn't seem to have a function for it.

Can someone give me an hint?

3
  • 3
    Look into NSSet or NSOrderedSet. Commented Dec 23, 2014 at 16:37
  • I use Dollar. $.uniq(array) github.com/ankurp/Dollar#uniq---uniq Commented Dec 6, 2016 at 19:00
  • No need to write extensions any more. Apple has provided uniqued() method in algorithms package. More info stackoverflow.com/a/70210590/1311902 Commented Dec 3, 2021 at 7:03

3 Answers 3

215

There might be a more efficient way, but an extension would probably be most straightforward:

extension Array where Element: Equatable {
    var unique: [Element] {
        var uniqueValues: [Element] = []
        forEach { item in
            guard !uniqueValues.contains(item) else { return }
            uniqueValues.append(item)
        }
        return uniqueValues
    }
}

If order doesn't matter and objects are also hashable:

let array = ["one", "one", "two", "two", "three", "three"]
// order NOT guaranteed
let unique = Array(Set(array))
// ["three", "one", "two"]
Sign up to request clarification or add additional context in comments.

8 Comments

The first example is clean and simple. Works in Swift 3. Thanks!
Array(Set()) works in Swift 4 as well. Great solution. Thanks.
Negative. You may get a random order by using set.
@BB9z I pointed that out in the answer, and also provided an extension that will preserve order, so don't know what you want here :)
First variant has bad performance characteristics on big data set. Be cautious.
|
77

There isn't a function to do this in the Swift standard library, but you could write one:

extension Sequence where Iterator.Element: Hashable {
    func unique() -> [Iterator.Element] {
        var seen: [Iterator.Element: Bool] = [:]
        return self.filter { seen.updateValue(true, forKey: $0) == nil }
    }
}

let a = ["four","one", "two", "one", "three","four", "four"]
a.unique // ["four", "one", "two", "three"]

This has the downside of requiring the contents of the sequence to be hashable, not just equatable, but then again most equatable things are, including strings.

It also preserves the original ordering unlike, say, putting the contents in a dictionary or set and then getting them back out again.

8 Comments

Is there an error? ` Use of unresolved identifier 'seq'`
Ah, so there is, should be filter(source)
This is throwing an error in Swift 2. It should be source.filter.
Not working to Swift 3. Are you have some ideas?
Set can be also used for seen var seen = Set<Element>() return filter { seen.update(with: $0) == nil }
|
4

I don't know of a built in way. This generic function would do it:

func distinct<S: SequenceType, E: Equatable where E==S.Generator.Element>(source: S) -> [E]
{
    var unique = [E]()

    for item in source
    {
        if !contains(unique, item)
        {
            unique.append(item)
        }
    }
    return unique
}

The downside here is that this solution runs in O(n2).

5 Comments

contains runs in O(n) so this solution runs in quadratic time (though it does have the benefit of not requiring the elements be hashable).
@AirspeedVelocity Yes that's a good point. And being hashable won't normally be a problem like you've pointed out in your solution. Nonetheless it is an alternate, albeit slower, solution if having to be hashable is a game breaker. I prefer your solution but I think I'm going to keep this posted for that reason.
Overloading means you can implement both, and the best one will be picked! (because Hashable conforms to Equatable, so is more specific, overload resolution will prefer it :)
(so long as you make both versions either take a sequence or an array, that is... otherwise they'll fight)
Yep! Gotta love Swift :) I'll edit my answer to take a sequence to be more generic and in case anyone wants to take the overloading approach.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.