I am trying to enumerate files in a Channel to rename them before using collectFile:
files.flatten().merge(Channel.fromList([1, 2, 3, 4])).collectFile(storeDir: "$SCRATCH/intermediate") {
item -> ["data${item[1]}.csv", item[0].text]
}
But the latest documentation says that the merge operator for channels is deprecated, but does not point to any alternative that should be used. What can I use instead of merge?
The migration notes say to use the join operator instead. If your inputs were lists, you could do something like:
def indexedChannel( items ) {
return Channel.from( items.withIndex() ).map { item, idx -> tuple( idx, item ) }
}
ch1 = indexedChannel( [ 15, 20, 21 ] )
ch2 = indexedChannel( [ 'a', 'b', 'c' ] )
ch3 = indexedChannel( [ 1, 2, 3 ] )
ch1
.join( ch2 )
.join( ch3 )
.view()
Results:
[0, 15, a, 1]
[1, 20, b, 2]
[2, 21, c, 3]
However, the merging/joining of two channels is unnecessary to enumerate. Just use the map operator:
def c = 1
Channel
.fromPath( './data/*.txt' )
.map { tuple( it, c++ ) }
.collectFile(storeDir: "$SCRATCH/intermediate") { fn, count ->
["data${count}.csv", fn.text]
}
Related
Is there some nice Kotlin idiom to efficiently iterate a list two items at a time?
Something like:
val items = listOf("one", "two", "three", "four")
items.forEach(2) { item1, item2 ->
println("$item1 and $item2")
}
and the output would be:
one and two
three and four
Probably the simplest way is to use chunked(2)
listOf(1, 2, 3, 4, 5, 6, 7)
.chunked(2)
.forEach { println(it) }
Prints:
[1, 2]
[3, 4]
[5, 6]
[7]
If you ensure the number of elements is even, you can access the pairs with named variables:
listOf(1, 2, 3, 4, 5, 6)
.also { require(it.size % 2 == 0) { "Number of elements must be even" } }
.chunked(2)
.forEach { (item1, item2) ->
println("$item1 and $item2")
}
Prints:
1 and 2
3 and 4
5 and 6
Is there any function (like fold, map, filter), which gets 2 arrays and lambda-function (for example multiplication) as parameters and returns third array?
I've used cycle for, but is there more beautiful method?
Yes, there is zip (nice example at the bottom of the page), see this (different) example:
fun main() {
val a = arrayOf( 1, 2, 3, 4 )
val b = arrayOf( 1, 2, 3, 4 )
val c = a.zip(b) { i, j -> i * j }
println(c)
}
which outputs
[1, 4, 9, 16]
There isn't a built in specifically but you can do this:
array1.zip(array2).map { (x,y) -> x*y }
The following is code from a LeetCode solution.
This is the description:
Given an array A of non-negative integers, half of the integers in A are odd, and half of the integers are even.
Sort the array so that whenever A[i] is odd, i is odd; and whenever A[i] is even, i is even.
I managed to write code that worked but mine was almost like Java but in Kotlin (a common problem - I know).
I found this code in the comments:
fun sortArrayByParityII(A: IntArray): IntArray {
val even = A.filter { it % 2 == 0 }
val odd = A.filter { it % 2 == 1 }
return even.zip(odd).flatMap { listOf(it.first, it.second) }.toIntArray()
}
I know that the first couple of line do. They simple filter the array into even and odd arrays.
I even understand (after looking up) what the "zip" does.
What I can't figure out is what this does:
flatMap { listOf(it.first, it.second) }
Let's look step by step:
fun main() {
val list = (1..10).toList()
val even = list.filter { it % 2 == 0 } // [2, 4, 6, 8, 10]
val odd = list.filter { it % 2 == 1 } // [1, 3, 5, 7, 9]
val zipped = even.zip(odd) // [(2, 1), (4, 3), (6, 5), (8, 7), (10, 9)]
val flatten = zipped.flatMap { listOf(it.first, it.second) } // [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
}
flatMap takes a function which returns a list and inserts elements of this list in to initial list. So [(2, 1), (4, 3)] becomes [2, 1, 4, 3]
given the filtered lists:
odd = [1,3,5,7,9,...]
even = [2,4,6,8,...]
the zip function concatenates each single item of each list into a list of tuples:
even.zip(odd)
// [(2,1),(4,3),(6,5),(8,7),...]
flat map here is doing the operation on each item(tuple) and returns a single list, it picks first then second item in each tuple and adds them into a single list:
even.zip(add).flatMap { listOf(it.first, it.second) }
// [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
I'm trying to find a way to get the product of a List or Array without using "repeat" or any loop on Kotlin but after some research I couldn't find anything similar.
Something like this in Python would be:
>>> reduce(lambda x, y: x*y, [1,2,3,4,5,6])
output: 720
You can use reduce in Kotlin.
From the doc:
Accumulates value starting with the first element and applying
operation from left to right to current accumulator value and each
element.
val list = listOf<Int>(1, 2, 3, 4, 5, 6)
val array = intArrayOf(1, 2, 3, 4, 5, 6)
list.reduce { acc, i -> acc * i } // returns 720
array.reduce { acc, i -> acc * i } // returns 720
An even simpler solution might be: (1..6).reduce(Int::times)
Use the fold function
val total = listOf(1, 2, 3, 4, 5).fold(1, { total, next -> total * next })
Hope this helps:
fun main(args: Array<String>){
val array = intArrayOf(1, 2, 3, 4, 5, 6)
val product = array.fold(1){acc, i -> acc * i}
println("The result is: $product")
}
This will output the product of the array.
Use the fold or reduce function. Both will work.
val array = arrayOf(1, 2, 3, 4, 5, 6)
println("Product of list: ${listOfMultiplication(array)}")
fun listOfMultiplication(array: Array<Int>): Int {
return array.reduce { total, next -> total * next }
}
val array = arrayOf(1, 2, 3, 4, 5, 6)
println("Product of list: ${listOfMultiplication(array)}")
fun listOfMultiplication(array: Array<Int>): Int {
return array.fold(1) { total, next -> total * next }
}
I have a sequence of interleaved data (with fixed stride) and I'd like to reduce it to a single value for each "structure" (n*stride values to n values).
I could just use loop writing into the mutable list with selected step for reader index, but I'm looking for more functional and readable approach. Any thoughts?
For example:
Input sequence consists of RGB triplets (stride 3) and output is grayscale.
Imperative way is like:
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 make something like that without explicitly implementing a function and mutable container, but purely on functional extensions like .map and so on?
Kotlin 1.2 (Milestone 1 was released yesterday) brings the chunked method on collections. It chunks up 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() }
A possible way would be grouping by the index of the elements (in this case /3) and mapping these groups to their sum.
stream.withIndex()
.groupBy { it.index / 3 }
.toSortedMap()
.values
.map { (it.sumBy { it.value } / 3).toByte() }
Also strictly functional, but using Rx, would be possible by using window(long)
Observable.from(stream)
.window(3)
.concatMap { it.reduce(Int::plus).toObservable() }
.map { (it / 3).toByte() }
Similar to #marstran's answer, in Kotlin 1.2 you can use chunked function, but providing the transform lambda to it:
fun greyscale(stream: List<Byte>): List<Byte> =
stream.chunked(3) { it.average().toByte() }
This variant has an advantage that it doesn't instantiate a new List for every triple, but rather creates a single List and reuses it during the entire operation.
Excludes remaining elements:
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 remaining 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]
Best what I'm capable of is this:
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 variations with ArrayList with add and last