Let say I have a Sequence<Int>
of unknown origin (not necessarily from a collection) and unknown but finite size:
val seq = sequenceOf(1, 2, -3, 4, 5, /* ... */)
Assume the sequence is large enough to make it undesirable to turn the whole sequence into a List
.
I want to get the last element of the sequence:
val last = seq.last()
But I also want to catch any "invalid" element that might appear (let's say negative numbers are invalid) and return the first such element:
val invalid = seq.first { it < 0 }
But how can I do both of those at the same time?
val lastUnlessInvalidElementPresent = seq.firstOrNull { it < 0 } ?: seq.last()
The problem is that ?: seq.last()
doesn't work because by the time firstOrNull
has returned null, the whole sequence has been consumed.
I can do this iteratively, but would prefer a functional solution.
I don't think this can be easily done with the built-in functions because last is a somewhat special predicate.
Adapting the existing lastOrNull
to test for the first occurrence of a predicate (it < 0
) would look like this:
inline fun <T> Sequence<T>.firstMatchOrLastOrNull(predicate: (T) -> Boolean): T? {
var last: T? = null
for (element in this) {
if (predicate(element)) return element
last = element
}
return last
}
Now you can use seq.firstMatchOrLastOrNull { it < 0 }
to get what you want. This shouldn't be different, performance-wise, from the built-in functions.
In the end I've come up with my own succinct solution:
fun <T> Sequence<T>.firstMatchOrLastOrNull(predicate: (T) -> Boolean): T? {
var last: T? = null
return onEach { last = it }.firstOrNull(predicate) ?: last
}
fun main() {
println(sequenceOf(1, 2, -3, 4, 5).firstMatchOrLastOrNull { it < 0 }) // -3
println(sequenceOf(1, 2, 3, 4, 5).firstMatchOrLastOrNull { it < 0 }) // 5
}
It's almost functional, but it's not pure functional because it uses a mutable variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With