Search code examples
swifthigher-order-functionsshort-circuiting

Does Swift have short-circuiting higher-order functions like Any or All?


I'm aware of Swift's higher-order functions like Map, Filter, Reduce and FlatMap, but I'm not aware of any like 'All' or 'Any' which return a boolean that short-circuit on a positive test while enumerating the results.

For instance, consider you having a collection of 10,000 objects, each with a property called isFulfilled and you want to see if any in that collection have isFulfilled set to false. In C#, you could use myObjects.Any(obj -> !obj.isFulfilled) and when that condition was hit, it would short-circuit the rest of the enumeration and immediately return true.

Is there any such thing in Swift?


Solution

  • Sequence (and in particular Collection and Array) has a (short-circuiting) contains(where:) method taking a boolean predicate as argument. For example,

    if array.contains(where: { $0 % 2 == 0 })
    

    checks if the array contains any even number.

    There is no "all" method, but you can use contains() as well by negating both the predicate and the result. For example,

    if !array.contains(where: { $0 % 2 != 0 })
    

    checks if all numbers in the array are even. Of course you can define a custom extension method:

    extension Sequence {
        func allSatisfy(_ predicate: (Iterator.Element) -> Bool) -> Bool {
            return !contains(where: { !predicate($0) } )
        }
    }
    

    If you want to allow "throwing" predicates in the same way as the contains method then it would be defined as

    extension Sequence {
        func allSatisfy(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool {
            return try !contains(where: { try !predicate($0) } )
        }
    }
    

    Update: As James Shapiro correctly noticed, an allSatisfy method has been added to the Sequence type in Swift 4.2 (currently in beta), see

    (Requires a recent 4.2 developer snapshot.)