Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 281
Mark A. Donohoe Avatar asked Jun 02 '17 20:06

Mark A. Donohoe


People also ask

What are the higher-order functions in swift?

Swift Higher-Order Functions Some of the best-known functions are map , compactMap , flatMap , reduce , filter , contains , sorted , forEach , and removeAll .

Is sort a higher-order function?

A higher-order function can be defined as a function that accepts one or more functions as arguments and returns a function as a result. In this article, we will discuss some swift higher-order functions, including forEach, map, CompactMap, flatMap, filter, reduce, sort, and sorted.


2 Answers

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

  • SE-0027 Add an allSatisfy algorithm to Sequence

(Requires a recent 4.2 developer snapshot.)

like image 148
Martin R Avatar answered Sep 27 '22 19:09

Martin R


One other thing that you can do in Swift that is similar to "short circuiting" in this case is to use the lazy property of a collection, which would change your implementation to something like this:

myObjects.lazy.filter({ !$0.isFulfilled }).first != nil

It's not exactly the same thing you're asking for, but might help provide another option when dealing with these higher-order functions. You can read more about lazy in Apple's docs. As of this edit the docs contain the following:

var lazy: LazyCollection> A view onto this collection that provides lazy implementations of normally eager operations, such as map and filter.

var lazy: LazySequence> A sequence containing the same elements as this sequence, but on which some operations, such as map and filter, are implemented lazily.

like image 40
creeperspeak Avatar answered Sep 27 '22 19:09

creeperspeak