I am trying to find the first two elements in a list that meet a condition (filtering), for that purpose I have implemented the following piece of code in kotlin:
val arr = 0 until 20
val res = arr.filter { i ->
println("Filter: $i")
i % 2 == 0
}.take(2)
Everything was fine until I realized it filters through the whole list, no matter if the two elements have been found.
Making use of Java 8 stream api, it works as expected.
val res2 = arr.toList().stream()
.filter { i ->
println("Filter: $i")
i % 2 == 0
}.limit(2)
So my questions is if it can be achieved using only Kotlin functions.
I know I could use a simple for loop but I want to use a functional programming aproach.
Kotlin, by default, does these kind of operations eagerly whereas Streams in Java are lazy. You can have the same behaviour in Kotlin if you work with sequences, which can easily be generated from Array
s or Iterable
s with asSequence()
.
arr.asSequence().filter { i ->
println("Filter: $i")
i % 2 == 0
}.take(2).toList()
//Filter: 0
//Filter: 1
//Filter: 2
Note that the sequence has to be transformed back to a list in the end.
You can read about the details here.
By using a sequence, you can get lazy evaluation like a Java 8 stream. I've rewritten your example with a lazy sequence and explicit types (I haven't changed them, I just declared them for clarity):
val arr: IntRange = 0 until 20
val res: List<Int> = arr
.asSequence()
.filter { i ->
println("Filter: $i")
i % 2 == 0
}
.take(2)
.toList()
// Prints
Filter: 0
Filter: 1
Filter: 2
And res == [0, 2]
By turning arr
into a sequence, performing your filter
and take
(which is like Java's limit
), and turning it back into a List, it will perform lazily.
From the documentation for Sequence:
A sequence that returns values through its iterator. The values are evaluated lazily, and the sequence is potentially infinite.
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