Fixed sequence, functional / threading Kotlin idiom
I have a sequence of striped data (fixed stride) and I would like to reduce it to one value for each "structure" (n * stride values ββto n values).
I could just use a loop write to a modified list with a selected step for the reader index, but I'm looking for a more functional and readable approach. Any thoughts?
For example: The input sequence is RGB triplets (step 3) and the output is in grayscale.
Imperative way:
fun greyscale(stream:List<Byte>):List<Byte>{
val out = ArrayList(stream.size / 3)
var i = 0; var o = 0
while(i < stream.size)
out[o++]=(stream[i++] + stream[i++] + stream[i++])/3
return out
}
How can I do something like this without explicitly implementing the function and mutable container, but only on functional extensions like .map
etc.?
source to share
Kotlin 1.2 (Milestone 1 was released yesterday) brings a method chunked
for collections. It splits the collection into blocks of a given size. You can use this to implement your function:
fun greyscale(stream: List<Byte>): List<Byte> =
stream.chunked(3)
.map { (it.sum() / 3).toByte() }
source to share
A possible way is to group by the index of the elements (in this case /3
) and match those groups with their sum.
stream.withIndex()
.groupBy { it.index / 3 }
.toSortedMap()
.values
.map { (it.sumBy { it.value } / 3).toByte() }
Also strictly functional, but using Rx one could use window(long)
Observable.from(stream)
.window(3)
.concatMap { it.reduce(Int::plus).toObservable() }
.map { (it / 3).toByte() }
source to share
As in @ marstran's answer, in Kotlin 1.2 you can use a function chunked
, but providing it with a lambda transformation:
fun greyscale(stream: List<Byte>): List<Byte> =
stream.chunked(3) { it.average().toByte() }
This option has the advantage that it does not instantiate a new list for each triplet, but creates a single list and reuses it throughout the entire operation.
source to share
Excludes other items:
const val N = 3
fun greyscale(stream: List<Byte>) = (0 until stream.size / N)
.map { it * N }
.map { stream.subList(it, it + N).sum() / N }
.map(Int::toByte)
Output
[1, 2, 3, 4, 5, 6] => [2, 5]
[1, 2, 3, 4, 5] => [2]
Includes other elements:
const val N = 3
fun greyscale(stream: List<Byte>) = (0 until (stream.size + N - 1) / N)
.map { it * N }
.map { stream.subList(it, minOf(stream.size, it + N)).sum() / N }
.map(Int::toByte)
Output
[1, 2, 3, 4, 5, 6] => [2, 5]
[1, 2, 3, 4, 5] => [2, 3]
source to share