Reduce array to set in Swift

You don't have to reduce an array to get it into a set; just create the set with an array: let objectSet = Set(objects.map { $0.URL }).


With Swift 5.1, you can use one of the three following examples in order to solve your problem.


#1. Using Array's map(_:) method and Set's init(_:) initializer

In the simplest case, you can map you initial array to an array of urls (String) then create a set from that array. The Playground below code shows how to do it:

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlArray = objectArray.map({ $0.url })
let urlSet = Set(urlArray)
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

#2. Using Array's reduce(into:_:) method

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.reduce(into: Set<String>(), { (urls, object) in
    urls.insert(object.url)
})
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

As an alternative, you can use Array's reduce(_:_:) method:

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.reduce(Set<String>(), { (partialSet, object) in
    var urls = partialSet
    urls.insert(object.url)
    return urls
})
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

#3. Using an Array extension

If necessary, you can create a mapToSet method for Array that takes a transform closure parameter and returns a Set. The Playground below code shows how to use it:

extension Array {

    func mapToSet<T: Hashable>(_ transform: (Element) -> T) -> Set<T> {
        var result = Set<T>()
        for item in self {
            result.insert(transform(item))
        }
        return result
    }

}

struct MyObject {
    let url: String
}

let objectArray = [
    MyObject(url: "mozilla.org"),
    MyObject(url: "gnu.org"),
    MyObject(url: "git-scm.com")
]

let urlSet = objectArray.mapToSet({ $0.url })
dump(urlSet)
// ▿ 3 members
//   - "git-scm.com"
//   - "mozilla.org"
//   - "gnu.org"

reduce() method expects a closure that returns a combined value, while insert() methods of Set value does not return anything but instead it inserts a new element into the existing set.

In order to make it work you would need to do something like:

objects.reduce(Set<String>()) {
    $0.union(CollectionOfOne($1.URL))
}

But the above is a bit of an unnecessary complication. If you have a big array, that would mean quite a number of ever-growing sets to be created while Swift goes over all the elements from objects. Better follow the advice from @NRitH and use map() as that would make a resulting set in one go.


Swift 1.0-2.x ONLY:

If URL on your object is a strongly-typed String, you can create a new Set<String> object and use unionInPlace on the set with the mapped array:

var mySet = Set<String>()
mySet.unionInPlace(objects.map { $0.URL as String })