Reputation: 477
Let's say I have an array of closures that I want to run on every UITouch. Here's the code I use:
touches.filter { touch in
return touch.phase == .Ended && touch.tapCount == 1
}.forEach { touch in
actionsOnTap.forEach { action in
action(touch)
}
}
It bugs me that there's nested forEach statement, and I guess there's some clean way that can be applied exactly for that case, but I can't think of it. Can anyone give me a hint?
Upvotes: 9
Views: 4803
Reputation: 299455
This is a good example of why forEach
is not a universal (or even appropriately common) replacement for for-in
. This code become shorter (140 chars vs 186 chars) and clearer just using a traditional for
loop:
for touch in touches where touch.phase == .Ended && touch.tapCount == 1 {
for action in actionsOnTap {
action(touch)
}
}
It also doesn't create an extra array copy the way the filter
does. This isn't a general reason not to use filter
. filter
is a very powerful tool that should be used often, but in this case, it's clearer and more efficient to use for
.
Edited to use @originaluser2's suggestion of where
rather than guard
. That probably is better Swift.
Upvotes: 6
Reputation: 80901
You should definitely eliminate the filter
from your logic and possibly use a guard
inside the first loop instead, for the sake of efficiency and conciseness. I also agree with @Rob's and @matt's suggestion of using a traditional for loop instead of forEach
– at least for the first loop.
Although a (maybe even cleaner) alternative is the integrate the touch conditional logic into the for
loop directly through using the where
clause, as well as possibly folding your forEach
into a single line (whichever you find more readable).
I'd write it like this:
for touch in touches where touch.phase == .Ended && touch.tapCount == 1 {
actionsOnTap.forEach{$0(touch)}
}
Upvotes: 9
Reputation: 535566
Personally, I like the nesting. I would write:
for touch in touches {
if touch.phase == .Ended {
if touch.tapCount == 1 {
actionsOnTap.forEach {$0(touch)}
}
}
}
To me, that's clean and (above all) clear.
Upvotes: 11
Reputation: 11250
As you have two heterogeneous array types. another solution that avoid the extra iteration that filter
do is filter yourself the touches you want to check.
touches.forEach{
guard $0.phase == .Ended && $0.tapCount == 1 else { return }
actions.forEach{ action in
action($0)
}
}
Upvotes: 1