Add Sequence.coalesce()
This commit is contained in:
parent
51d14216e9
commit
a8137c6ee4
|
@ -1,2 +1,4 @@
|
|||
gradle*
|
||||
.gradle/
|
||||
build/
|
||||
out/
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package moe.kageru.sekwences
|
||||
|
||||
/**
|
||||
* Returns a sequences that optionally merges adjacent elements.
|
||||
* Heavily inspired by Itertools.coalesce() from the Rust crate.
|
||||
*
|
||||
* https://docs.rs/itertools/0.8.0/itertools/trait.Itertools.html#method.coalesce
|
||||
*/
|
||||
fun <T> Sequence<T>.coalesce(merger: (T, T) -> T?): Sequence<T> {
|
||||
return if (this.iterator().hasNext()) {
|
||||
CoalescingSequence(this, merger)
|
||||
} else {
|
||||
emptySequence()
|
||||
}
|
||||
}
|
||||
|
||||
class CoalescingSequence<T>(private val source: Sequence<T>, private inline val merger: (T, T) -> T?) : Sequence<T> {
|
||||
override fun iterator(): Iterator<T> {
|
||||
return CoalescingIterator(source.iterator(), merger)
|
||||
}
|
||||
}
|
||||
|
||||
class CoalescingIterator<T>(private val source: Iterator<T>, private inline val merger: (T, T) -> T?) : Iterator<T> {
|
||||
private var previous: T = if (source.hasNext()) source.next() else error("Please don’t pass empty iterators in here")
|
||||
private var hasNext = true
|
||||
|
||||
override fun hasNext() = hasNext
|
||||
|
||||
override tailrec fun next(): T {
|
||||
if (!source.hasNext()) {
|
||||
hasNext = false
|
||||
return previous
|
||||
}
|
||||
|
||||
val current = source.next()
|
||||
val merged = merger(previous, current)
|
||||
// If the elements can’t be merged, return the first, then save the second for next time.
|
||||
return if (merged == null) {
|
||||
val ret = previous
|
||||
previous = current
|
||||
ret
|
||||
} else {
|
||||
previous = merged
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package moe.kageru.sekwences
|
||||
|
||||
import io.kotlintest.shouldBe
|
||||
import io.kotlintest.specs.ShouldSpec
|
||||
|
||||
class SekwencesTest : ShouldSpec({
|
||||
"should merge numbers" {
|
||||
val input = sequenceOf(1, 2, 3, 4, 5, 6, 4)
|
||||
val output = input.coalesce { first, second ->
|
||||
val sum = first + second
|
||||
if (sum <= 10) sum else null
|
||||
}.toList()
|
||||
output shouldBe listOf(10, 5, 10)
|
||||
}
|
||||
|
||||
"should merge collections" {
|
||||
val input = sequenceOf("apple", "apricot", "banana", "peach", "pear", "plum")
|
||||
val output = input.map { listOf(it) }.coalesce { first, second ->
|
||||
if (first.first()[0] == second.first()[0]) {
|
||||
first + second
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.toList()
|
||||
output shouldBe listOf(listOf("apple", "apricot"), listOf("banana"), listOf("peach", "pear", "plum"))
|
||||
}
|
||||
|
||||
"should handle empty sequences" {
|
||||
val input = emptySequence<Int>()
|
||||
val output = input.coalesce { _, _ -> 0 }
|
||||
output shouldBe emptySequence()
|
||||
}
|
||||
|
||||
"should handle single elements" {
|
||||
val input = sequenceOf(1)
|
||||
val output = input.coalesce { a, b -> if (a > 5) a else b }.toList()
|
||||
output shouldBe listOf(1)
|
||||
}
|
||||
|
||||
"should not modify sequences that can’t be merged" {
|
||||
val input = listOf(6, 7, 34, 8)
|
||||
val output = input.asSequence().coalesce { first, second ->
|
||||
val sum = first + second
|
||||
if (sum <= 10) sum else null
|
||||
}.toList()
|
||||
output shouldBe input
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user