Reputation: 3864
I'd like to optimize my functions which rely on filter(). In some cases I want to break out of them when they reach a certain elements. (For example, I may have an array of distinct elements. Or, I just want to implement a kind of findFirst functionality.) In such instances it seems inefficient for the function to continue until the end of the array.
This is something very easy with loops, but I would like to apply the optimisations to functional programming principals. (The compiler wouldn't be able to perform such a optimization by itself, since it doesn't know my array and my intention.)
Can this be done?
Upvotes: 10
Views: 6330
Reputation: 3172
There is first(where:)
that breaks out when it finds the first passing case:
let data = ["Alpha","Beta","Gamma","Delta"]
let b2 = data.first(where:{$0=="Beta"})
Upvotes: 18
Reputation: 40963
filter
isn’t written to break out like that. I don’t believe there’s an out-of-the-box way to do the kind of thing you’re looking for.
Generally it’s better to avoid making functions more flexible to cover narrow cases. Adding early bail might be useful sometimes but would come at the cost of complicating filter
, and the code would probably be hard to read (bear in mind one of the main goals of using functions like filter
, map
etc is to make the code easier to read and to be certain the code is correct). Some functions do support early exit though, when it’s fundamental to their purpose - for example, contains
or indexOf
.
But it’s not too hard to create your own higher-order functions to do what you want, the name of which makes their intent pretty clear. For example, to take all the elements in a sequence up to the first one that doesn’t match a pattern, you could write takeWhile
like this:
extension SequenceType {
func takeWhile(condition: Generator.Element -> Bool) -> [Generator.Element] {
var result: [Generator.Element] = []
for x in self {
guard condition(x) else { break }
result.append(x)
}
return result
}
}
let nums = [1,3,1,2]
let isOdd = { $0%2 == 1 }
let initialOdd = nums.takeWhile(isOdd)
print(initialOdd)
Upvotes: 9
Reputation: 5722
The point of using filter is to factor out the predicate and scan the whole collection. You can't find out which data to filter till you went thru the whole data set. In its basic form filter is just a check on the a condition (predicate) to construct a new collection:
let bools = [true, false, false, true]
print(bools.filter {$0})
What you are asking is gaining control of the iterator loop in filter while filter is implemented to simplify it by hiding it. I suggest you use the control flow statements built in the language (if, for, etc, continue, break) to give you what you need. No point forcing weird syntax statements that make your code looks more convoluted and ungrokable.
Upvotes: 3