Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zipWithNext for Flow #1767

Open
ThanosFisherman opened this issue Jan 19, 2020 · 9 comments
Open

zipWithNext for Flow #1767

ThanosFisherman opened this issue Jan 19, 2020 · 9 comments
Labels

Comments

@ThanosFisherman
Copy link

Can we have a zipWithNext operator for Flow? Could you help me implement one?

@elizarov
Copy link
Contributor

Implementation is pretty straight-forward, but what's your use-case? Why do you need it?

fun <T, R> Flow<T>.zipWithNext(transform: (T, T) -> R): Flow<R> = flow {
    var prev: Any? = UNDEFINED
    collect { value ->
        if (prev !== UNDEFINED) emit(transform(prev as T, value))
        prev = value
    }
}

private object UNDEFINED

@elizarov elizarov added the flow label Jan 22, 2020
@ThanosFisherman
Copy link
Author

ThanosFisherman commented Jan 22, 2020

Thanks for helping out @elizarov

Well is not something that can't be done differently. I'm new to Coroutines but I am building a bluetooth library on Android using Coroutines/Flow.

As such during the readout response, which btw happens byte-by-byte via Flow, I need to check whether a specific pair of Hex bytes (02 FF) is included in the response when eventually collecting those bytes.

But I came up with an alternative using buffers and accumulating the whole Bluetooth Response into a ByteArray before it's collected by Flow. So in that case collect gives me a ByteArray instead of Byte so I can easily search for those two pair of values

@elizarov
Copy link
Contributor

elizarov commented Mar 17, 2020

Good that you've found a better way. zipWithNext is a bad way to do it and using flow for byte-by-byte transfer is not ideal either.

@fluidsonic
Copy link

fluidsonic commented Oct 11, 2020

I would've found this useful for diffing upstream values into downstream change events.

datasets // DataSet1… DataSet2… DataSet3… DataSet… DataSet… 
   .onStart { emit(DataSet.empty) } // DataSet.empty… DataSet1… DataSet2… DataSet3… DataSet… DataSet… 
   .zipWithNext { a, b -> b.changesSince(a) } // [Change1a, Change1b, Change1c]… [Change2a, Change2b]…
   .flatMapConcat { changes -> changes.asFlow() } // Change1a… Change1b… Change1c… Change2a… Change2b… 

@elizarov elizarov reopened this Oct 14, 2020
@elizarov
Copy link
Contributor

@fluidsonic Thanks for a use-case.

@chachako
Copy link

@elizarov Should it be added to core? I currently need to merge the values of each emission, or is there a better way?

@elizarov
Copy link
Contributor

@rinorz You can use this implementation for now -> #1767 (comment)

@chachako
Copy link

@rinorz You can use this implementation for now -> #1767 (comment)

Yes, I am currently doing this, I mean it might be added to the coroutine core, maybe it is useful

@matejdro
Copy link

matejdro commented Mar 9, 2022

Another use case: differentiating between existing state and between state changes.

For example, we have StateFlow<Boolean> in our app that emits true if user is logged in and false if user is logged out. For some operation we want to receive an event when user has just logged in. Just collecting the flow normally and triggering on true value is not helpful, because value could be true from the start (user could be logged in at the app startup). However, using zipWithNext allows us to know when value has changed from false to true.

We are using a bit modified version of the above implementation, where transform is also called on the first emission (but first parameter is null), so we can also get the initial value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants