Reduce array to set in Swift

swift reduce
swift set
convert array to set swift 4
swift array
swift reduce dictionary
swift remove duplicates from array
swift sort array
swift set order

I am trying to reduce an array of objects to a set in Swift and this is my code:

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

However, I get an error:

Type of expression is ambiguous without more context.

I do not understand what the problem is, since the type of URL is definitely String. Any ideas?

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 }).

reduce(_:_:), Swift Standard Library func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result For example, you can use this method on an array of numbers to find their sum or product. I have an array and want to recursively perform a specific action on all elements and get one single value/result. Recipe // Initialize the Array var a = [1,2,3,4,5,6] // Recursively perform the defined action ($0 + $1) // to all elements and get result var sum = a.reduce(0) { $0 + $1 } print(sum)

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"

Swift Guide to Map Filter Reduce, Take a few minutes to learn about Swift map, filter and reduce operations. use it with a Dictionary or a Set , the result will always be an Array . Use the reduce(_: _:) method to produce a single value from the elements of an entire sequence. For example, you can use this method on an array of numbers to find their sum or product. The next Partial Result closure is called sequentially with an accumulating value initialized to initial Result and each element of the sequence. This example

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.

Higher order functions in Swift: Filter, Map, Reduce, flatmap, lengthInMeters is a set. lengthInFeet is an array. What if you want to know the index of the collection while applying map to it?? Answer is simple  This method is preferred over reduce(_: _:) for efficiency when the result is a copy-on-write type, for example an Array or a Dictionary. The update Accumulating Result closure is called sequentially with a mutable accumulating value initialized to initial Result and each element of the sequence.

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 })

Reducing to Swift sets, A friend asked me, “Is there a better way to reduce to a set than .reduce the intermediate results into an array before creating a set (also no). The reduce() method is designed to convert a sequence into a single value, which makes it perfect for calculating the total of an array of numbers. For example, given the following number array: let numbers = [1, 12, 2, 9, 27] We could calculate the total of all those numbers like this: let total = numbers.reduce(0, +)

Swift Basics: reduce(), Say we wish to sum an array of numbers to find the total. Without the reduce function, we might use a foreach loop: let valuesToReduce: [Int]  In Swift, you can use Map, Reduce and Filter to loop over collection types like Array and Dictionary without using a for-loop. When building apps you typically use the procedural or object-oriented way of programming.

How to sum an array of numbers using reduce(), The reduce() method is designed to convert a sequence into a single value, which makes it perfect for calculating the total of an array of  Using map, filter or reduce to operate on Swift collection types such as Array or Dictionary is something that can take getting used to. Unless you have experience with functional languages your instinct may be to reach for the more familiar for-in loop. With that in mind here is my guide to using map, filter, reduce (and flatMap and compactMap).

Swift, Swift has a number of collections like Objective-C has and all these Collection types are inherited from it Collection, like Array, Set, Dictionary  You can create a set with any element type that conforms to the Hashable protocol. By default, most types in the standard library are hashable, including strings, numeric and Boolean types, enumeration cases without associated values, and even sets themselves. Swift makes it as easy to create a new set as to create a new array.

Comments
  • I think the signature for reduce is func reduce<T>(_ initial: T, @noescape combine combine: (T, Self.Generator.Element) throws -> T) rethrows -> T which is not what you're passing.
  • But I do not want the set of objects, but the set of different URLs that these objects have. I guess I could do a map and then Set(map), but I should be able to achieve this with reduce.
  • Ah. Then let objectSet = Set(objects.map { $0.URL }).
  • Ah, okay, great. Could you maybe edit your answer so I can accept it?
  • It depends. If you map/flatmap its nice to chain calls.
  • Thanks for this answer, it clarifies the problem for me.
  • @Banana, np. See my edited answer. It turns out to be not only over-complicating but also an inefficient thing to do.