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.
You can use fold()
with a simple data class that holds your value plus a flag indicating whether you're still in "get last" mode or already in "encountered an invalid value, let's keep it" mode:
data class Elem(val value: Int, val valid: Boolean)
fun main() {
val seq = sequenceOf(1, 2, -3, 4, 5, /* ... */)
val initial = Elem(0, true)
val lastUnlessInvalidElementPresent = seq.fold(Elem(0, true),
{ acc, item -> if (acc.valid && item < 0) { Elem(item, false) } // first invalid element
else if (acc.valid) { Elem(item, true) } // valid element, keep looking
else { acc } // already found an invalid element - keep it
}
)
println(lastUnlessInvalidElementPresent)
}
This approach has two disadvantages, though:
fold()
will still consume the complete sequence even after encountering an invalid element