What would you think about an indexed `Sequence.ta...
# stdlib
s
What would you think about an indexed
Sequence.takeWhile
? E.g. one of these:
Copy code
fun <T>Sequence<T>.takeThenWhile(count: Int, predicate: (T) -> Boolean): Sequence<T> {
    var i = 0
    return this.takeWhile{ i < count || predicate(it) }.onEach { i++ }
}


fun <T>Sequence<T>.takeWhileIndexed(predicate: (Int, T) -> Boolean): Sequence<T> {
    var i = 0
    return takeWhile { predicate(i, it) }.onEach { i++ }
}
Both
take
and
takeWhile
could be implemented in terms of this function
Copy code
fun <T>Sequence<T>.take(count: Int) = takeThenWhile(count) { false }
fun <T>Sequence<T>.takeWhile(predicate: (T) -> Boolean) = takeThenWhile(0, predicate)

fun <T>Sequence<T>.take(count: Int) = takeWhileIndexed { i, _ -> i < count }
fun <T>Sequence<T>.takeWhile(predicate: (T) -> Boolean) = takeWhileIndexed { _, t -> predicate(t) }
g
Why not using
withIndex()
?
Copy code
sequenceOf("a").withIndex().takeWhile { (index, value) -> }
s
derp, that works; I missed
withIndex
😅
Hmm, although in this case I need to get back to the raw values anyway, and I'm not sure that I like having to wrap and then unwrap each value to do that, it's basically
Copy code
fun <T>Sequence<T>.takeThenWhile(count: Int, predicate: (T) -> Boolean): Sequence<T> {
    return withIndex().takeWhile{ (i, t) -> i < count || predicate(t) }.map { it.value }
}
e
btw using a mutable var like that is not good, if the sequence is iterated over multiple times
s
It won't be set separately for each invocation?
(In my usage, it's only iterated once, but I'd like to leave a comment if that's something to consider in the future)
e
not if
val s = .takeWhileIndexed(); s.forEach(); s.forEach()
. you could use the sequence builder,
Copy code
fun <T> Sequence<T>.takeWhileIndexed(count: Int, predicate: (Int, T) -> Boolean): Sequence<T> = sequence {
    if (count <= 0) return@sequence
    var i = 0
    for (item in this@takeWhileIndexed) {
        if (!predicate(i, item)) break
        yield(item)
        if (++i >= count) break
    }
}
in which case the variable is scoped to each iterator
that being said, how is your
takeWhileThen
any different from
.take.takeWhile
?
oh I see, || not &&
s
*`takeThenWhile` . I could name it more descriptively as
takeThenTakeWhile
.
The usage is
Copy code
private fun <T> Sequence<T>.countUnique(minItems: Int, uniqueValues: Int): Map<T, Int> {
    val counts = mutableMapOf<T, Int>()
    val items = takeThenWhile(minItems) { counts.size < uniqueValues }.filterNotNull()
    return items.groupingBy { it }.eachCountTo(counts)
}
Counts the first
minItems
, but if it finds fewer than
uniqueValues
, it'll keep going until it finds that many (or the sequence ends, obviously)
I think regular old
take
might have the same problem with double iteration? https://github.com/JetBrains/kotlin/blob/b65c477e68d2e4e3b635d63b7cdc61f41829d472/libraries/stdlib/src/kotlin/collections/Sequences.kt#L397 (The actual extension function is just slightly larger than
Sequence.take(n: Int) = TakeSequence(this, n)
)
Renamed to
takeAtLeastWhile
:)
e
stdlib
take
is fine, it's doing the same thing at a lower level
🤔 1
s
Oh, I think I see. The sdlib is safe because it creates a new iterator (on line 411— when I originally read it, I missed that it was a function call, not a property access). And I guess
sequence {}
does the same thing.