No support for zipping multiple Flows in Kotlin? - kotlin

I was really surprised when we were trying to move away from RxJ to kotlin Flows that there isn't any facility to zip multiple flows.
val flow1= (1..3).asFlow().onEach {delay(1000)}
val flow2= (1..3).asFlow().onEach {delay(2000)}
val flow3= (1..3).asFlow().onEach {delay(3000)}
I was looking for a Rx styled Flow.zip(flow1,flow2,flow3) however I failed to find any such facility.
What is strange to me that I didn't find many questions asking what I am asking here on stack or in any Kotlin Flows tutorial .
This makes me think that I must be doing something wrong , and that there might be an alternate facility to zip multiple flows.
Any hints ?

You can build your own on top of zip for two flows
inline fun <A, B, C, D> Flow<A>.zip(
flowB: Flow<B>,
flowC: Flow<C>,
crossinline f: (A, B, C) -> D
): Flow<D> =
zip(flowB, ::Pair).zip(flowC) { (a, b), c -> f(a, b, c) }
Usage:
suspend fun main() {
val one = flowOf(0, 1, 2, 3)
val two = flowOf("a", "b", "c", "d")
val three = flowOf(5.0, 6.0, 7.0)
one.zip(two, three, ::Triple).collect(::print)
// prints (0, a, 5.0)(1, b, 6.0)(2, c, 7.0)
}
Probably not as efficient as a direct implementation but depending on your use case it might be good enough.

Related

Kotlin: iterate through array in reversed order

Is there a convenient way in Kotlin to iterate through an array, let's say IntArray, in reversed order with these 2 conditions:
do not create an additional reversed copy of the array.
I need a handle to an element like in Java's enhanced for.
The best I could get is adding an extension function, but this needs to be done for each type of array if I need it not only for IntArrays:
fun IntArray.forEachReversed(action: (Int) -> Unit): Unit {
for (i in indices.reversed()) action(this[i])
}
Is there a better way in Kotlin class library?
this needs to be done for each type of array if I need it not only for IntArrays:
I think this is unavoidable because of the way the JVM works. There are separate classes to represent each primitive type on the JVM. However, there are only 8 of them, so it shouldn't be too bad ;-)
For Collections, there is the asReversed() function, but it's not available for arrays:
val original = mutableListOf('a', 'b', 'c', 'd', 'e')
val originalReadOnly = original as List<Char>
val reversed = originalReadOnly.asReversed()
println(original) // [a, b, c, d, e]
println(reversed) // [e, d, c, b, a]
// changing the original list affects its reversed view
original.add('f')
println(original) // [a, b, c, d, e, f]
println(reversed) // [f, e, d, c, b, a]
To answer you question, you solution looks fine but if your are targeting primitive IntArray, LongArray, FloatArray etc you cannot come with a generic solution, as this classes are independent and only thing common is Iterator, but you cannot traverse the iterator in reverse order without making a copy(ListIterator supports reverse iteration though), but the closest you can get is to use Array<T> instead specific Array like below
fun <T> Array<T>.forEachReversed(action: (T) -> Unit){
for(i in indices.reversed()){ action(this[i]) }
}
val intArray = Array(2){ 0 }
val longArray = Array<Long>(2){ 0 }
intArray.forEachReversed { }
longArray.forEachReversed { }
As pointed out by #ajan.kali if you need primitive arrays there is not much you can do. I suppose you have to deal with arrays but, if this is not the case, you should prefer other data structures (more info here)
Returning to your question, if your are fine using generic arrays you could probably declare your iterator to iterate in reverse order:
class ReverseIterator<T>(val it: Iterable<T>) : Iterator<T> {
private var index = it.count() - 1
override fun hasNext() = index >= 0
override fun next(): T = try { it.elementAt(index--) } catch (e:
IndexOutOfBoundsException) { index -= 1; throw
NoSuchElementException(e.message) }
}
then your extension function will become:
fun <T> Iterable<T>.forEachReversed(action: (T) -> Unit) {
for(elem in ReverseIterator(this)) {
action(elem)
}
}
and then given an array you can invoke it this way:
intArrayOf(1, 2, 3).asIterable().forEachReversed {
println(it)
}
Not particularly happy with this, but with arrays there is not much you can do other to try avoiding them.

Chaining Lambdas - Kotlin

I'm working on a problem from the Kotlin Apprentice book in the Lambdas chapter. Here is the problem:
Using the same nameList list, first filter the list to contain only names which
have more than four characters in them, and then create the same concatenation
of names as in the above exercise. (Hint: you can chain these operations
together.)
How can I chain the two lambdas together? Below is my code with separate lines for the 2 lambdas.
fun main() {
val nameList = listOf("John", "Jacob", "Jingleheimer", "Schmidt")
val onlyLongNames = nameList.filter {it.length > 4}
val myNameToo = onlyLongNames.fold("") {a, b -> a + b}
println(myNameToo)
}
fun main() {
val nameList = listOf("John", "Jacob", "Jingleheimer", "Schmidt")
val myNameToo = nameList.filter {it.length > 4}.fold("") {a, b -> a + b}
println(myNameToo)
}

Understanding Validated.applicative in kotlin arrow library

I come across below generic function which takes two Either type and a function as an argument. If both arguments are Either.Right then apply the function over it and returns the result, if any of the argument is Either.Left it returns NonEmptyList(Either.Left). Basically it performs the independent operation and accumulates the errors.
fun <T, E, A, B> constructFromParts(a: Either<E, A>, b: Either<E, B>, fn: (Tuple2<A, B>) -> T): Either<Nel<E>, T> {
val va = Validated.fromEither(a).toValidatedNel()
val vb = Validated.fromEither(b).toValidatedNel()
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
}
val error1:Either<String, Int> = "error 1".left()
val error2:Either<String, Int> = "error 2".left()
val valid:Either<Nel<String>, Int> = constructFromParts(
error1,
error2
){(a, b) -> a+b}
fun main() {
when(valid){
is Either.Right -> println(valid.b)
is Either.Left -> println(valid.a.all)
}
}
Above code prints
[error 1, error 2]
Inside the function, it converts Either to ValidatedNel type and accumulates both errors
( Invalid(e=NonEmptyList(all=[error 1])) Invalid(e=NonEmptyList(all=[error 2])) )
My question is how it performs this operation or could anyone explain the below line from the code.
return Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
Let's say I have a similar data type to Validated called ValRes
sealed class ValRes<out E, out A> {
data class Valid<A>(val a: A) : ValRes<Nothing, A>()
data class Invalid<E>(val e: E) : ValRes<E, Nothing>()
}
If I have two values of type ValRes and I want to combine them accumulating the errors I could write a function like this:
fun <E, A, B> tupled(
a: ValRes<E, A>,
b: ValRes<E, B>,
combine: (E, E) -> E
): ValRes<E, Pair<A, B>> =
if (a is Valid && b is Valid) valid(Pair(a.a, b.a))
else if (a is Invalid && b is Invalid) invalid(combine(a.e, b.e))
else if (a is Invalid) invalid(a.e)
else if (b is Invalid) invalid(b.e)
else throw IllegalStateException("This is impossible")
if both values are Valid I build a pair of the two values
if one of them is invalid, I get a new Invalid instance with the single value
if both are invalid, I use the combine function to build Invalid instance containing both values.
Usage:
tupled(
validateEmail("stojan"), //invalid
validateName(null) //invalid
) { e1, e2 -> "$e1, $e2" }
This works in a generic way, independent of the types E, A and B. But it only works for two values. We could build such a function for N values of type ValRes.
Now back to arrow:
Validated.applicative<Nel<E>>(NonEmptyList.semigroup()).map(va, vb, fn).fix().toEither()
tupled is similar to map (with hardcoded success function). va and vb here are similar to a and b in my example. Instead of returning a pair of values, here we have a custom function (fn) that combines the two values in case of success.
Combining the errors:
interface Semigroup<A> {
/**
* Combine two [A] values.
*/
fun A.combine(b: A): A
}
Semigroup in arrow is a way for combining two values from the same type in a single value of that same type. Similar to my combine function. NonEmptyList.semigroup() is the implementation of Semigroup for NonEmptyList that given two lists adds the elements together into a single NonEmptyList.
To sum up:
If both values are Valid -> it will combine them using the supplied function
If one value is Valid and one Invalid -> gives back the error
If both values are Invalid -> Uses the Semigroup instance for Nel to combine the errors
Under the hood this scales for 2 up to X values (22 I believe).

Kotlin `with` with multiple params

I really like how with behaves. Is it possible to extend with so it works with multiple params.
I want to use with like this.
with(foo, bar){
fooFunction()
barFunction()
}
First, I strongly against it.
One can come close to what you want:
data class A(val a: Int)
data class B(val b: Int)
fun<S, T> withPair(a: S, b: T, f: S.() -> T.() -> Unit) {
val g = a.f()
b.g()
}
fun main() {
withPair(A(1), B(2)) {{
print(a)
print(b)
}}
}
So you can have a block function which return a block function. You need nested lambdas though.
I don't think it's possible to write a function which behaves like this, but with the standard with you can write
with(foo) {
with(bar) {
fooFunction()
barFunction()
}
}
(note that if a method is available on both foo and bar, this way the bar method will be called).
Another possibility is to use a Pair, something like:
with( Pair("abc" , listOf(1,2,3)) ) {
println(first.plus("d"))
println(second.reversed())
}
Prints:
abcd
[3, 2, 1]
It's impossible to do this with the standard with function because it cannot have two receiver types of the lambda (accessed by this).
with(foo, bar){
fooFunction() //`this` would have to mean `foo`
barFunction() //`this` would have to mean `bar`
}
Nesting two withs is nasty. It can cause all sort of issues, with this ambiguity...
That said, you can create your own with function that will work similarly to the standard one, but will not use passed extension fun (so no receiver type "hidden" behind this), but a regular lambda with two arguments:
inline fun <T1, T2, R> with(t1: T1, t2: T2, block: (T1, T2) -> R): R {
return block.invoke(t1, t2)
}
//then:
with(foo, bar) { f, b ->
f.fooFunction()
b.barFunction()
}

Idiomatic way to convert a List to a Pair in Kotlin

There is an idiomatic approach to converting a Pair into a List:
Pair(a, b).toList()
No I am searching for the opposite process. My best approach looks like this:
Pair(list[0], list[1])
My problem with this is that I need to make a List value in code first for this to work. I would love something like this:
listOf(a, b).toPair()
For a more general solution you could use the extension function zipWithNext* which
Returns a list of pairs of each two adjacent elements in this collection.
The example in the documentation explains it better:
val letters = ('a'..'f').toList()
val pairs = letters.zipWithNext()
println(letters) // [a, b, c, d, e, f]
println(pairs) // [(a, b), (b, c), (c, d), (d, e), (e, f)]
*note that this function is available since v1.2 of Kotlin.
You can make this extension for yourself:
fun <T> List<T>.toPair(): Pair<T, T> {
if (this.size != 2) {
throw IllegalArgumentException("List is not of length 2!")
}
return Pair(this[0], this[1])
}
The error handling is up to you, you could also return a Pair<T, T>? with null being returned in the case where the List is not the correct length. You could also only check that it has at least 2 elements, and not require it to have exactly 2.
Usage is as you've described:
listOf(a, b).toPair()
Here's a variation on #zsmb13's solution that avoids creating the exception explicitly and dereferencing the list by index:
fun <T> List<T>.toPair(): Pair<T, T> {
require (this.size == 2) { "List is not of length 2!" }
val (a, b) = this
return Pair(a, b)
}
If you're looking for an option that is chainable (for example I came across this when mapping a list of strings that I turned into a pair by splitting), I find this pattern useful:
yourList.let{ it[0] to it[1] } // this assumes yourList always has at least 2 values
Here it is in context:
val pairs: List<Pair<String,String>> = lines.map{ it.split(",").let{ it[0] to it[1] } }