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.?

+3


source to share


5 answers


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() }

      

+3


source


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() }

      

+2


source


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.

+1


source


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]

0


source


The best I can do is:

fun grayscale(rgb:List<Byte>):List<Byte>
    = rgb.foldIndexed(
        IntArray(rgb.size / 3),
        { idx, acc, i -> 
            acc[idx / 3] = acc[idx / 3] + i; acc
        }).map{ (it / 3).toByte() }

      

Output

in: [1, 2, 3, 4, 5, 6]

out: [2, 5]

And options with ArrayList

with add

andlast

0


source







All Articles