Last week, some of you may have noticed that our groupByUser
function used the same "test" closure twice, once for prefix(while:)
and once for drop(while:)
. It kind of seems like these two are doing the same thing, right? We are splitting the array at a certain point; one time we want the subsequence before the split, the other time we want the subsequence after the split. In our case, we actually want both.
I know what you're thinking, and yes, there is a function for this! In languages like Haskell and Scala, it's called span
. It doesn't exist in Swift yet, but we can make one ourselves with the new sequence methods we just talked about.
extension Sequence {
func span(_ p: (Iterator.Element) throws -> Bool) rethrows -> (SubSequence, SubSequence) {
return (
try self.prefix(while: p),
try self.drop(while: p)
)
}
}
This makes our groupByUser
function much cleaner:
func groupByUser(_ messages: [Message]) -> [[Message]] {
guard let firstMessage = messages.first, messages.count > 1 else {
return [messages]
}
let (firstGroup, rest) = messages.span { $0.user == firstMessage.user }
return [Array(firstGroup)] + groupByUser(Array(rest))
}
👌
Where do the `throws` and `rethrows` come from in your declaration of `span`? No evidence of such in the predecessor post which is why I’m confused. Thanks!
The full signature of
drop(while:)
ispublic func drop(while predicate: (Iterator.Element) throws -> Bool) rethrows -> SubSequence
. It’s basically saying that if you pass in a predicate that throws, it can re-throw that error. Sincespan
is building on this, we also should accept predicates that throw. However, we don’t want to handle the errors ourselves, since we don’t know what kinds of errors might be possible, so we re-throw the error to the caller, who should be aware of what errors the predicate it passes in can throw.You can see the code for
drop(while:)
here, and the code forprefix(while:)
a little below it.