获取流程中的最新和先前的值

发布于 2025-02-07 08:16:22 字数 546 浏览 3 评论 0原文

我需要处理流程收集中的当前和以前的值,因此我需要一些这样的运算符:

----A----------B-------C-----|--->

---(null+A)---(A+B)---(B+C)--|--->

一个想法是类似的:

fun <T: Any> Flow<T>.withPrevious(): Flow<Pair<T?, T>> = flow {
    var prev: T? = null
    [email protected] {
        emit(prev to it)
        prev = it
    }
}

但是,这种方式无法控制第一个流的上下文。有更灵活的解决方案吗?

I need to handle current and previous value in flow collect, so I need some operator that acts like that:

----A----------B-------C-----|--->

---(null+A)---(A+B)---(B+C)--|--->

One idea is something like:

fun <T: Any> Flow<T>.withPrevious(): Flow<Pair<T?, T>> = flow {
    var prev: T? = null
    [email protected] {
        emit(prev to it)
        prev = it
    }
}

But this way there is no control over a context in which first flow will be executed. Is there more flexible solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦回梦里 2025-02-14 08:16:22

There's an operator which makes this very easy: runningFold

data class History<T>(val previous: T?, val current: T)

// emits null, History(null,1), History(1,2)...
fun <T> Flow<T>.runningHistory(): Flow<History<T>?> =
    runningFold(
        initial = null as (History<T>?),
        operation = { accumulator, new -> History(accumulator?.current, new) }
    )

// doesn't emit until first value History(null,1), History(1,2)...
fun <T> Flow<T>.runningHistoryAlternative(): Flow<History<T>> =
    runningHistory().filterNotNull()

可能需要调整无效以适合您的用途酶

您 显性

There's an operator which makes this very easy: runningFold

The docs have an example on how to use it to collect each emission of a flow; this can be easily adapted to fit our needs

data class History<T>(val previous: T?, val current: T)

// emits null, History(null,1), History(1,2)...
fun <T> Flow<T>.runningHistory(): Flow<History<T>?> =
    runningFold(
        initial = null as (History<T>?),
        operation = { accumulator, new -> History(accumulator?.current, new) }
    )

// doesn't emit until first value History(null,1), History(1,2)...
fun <T> Flow<T>.runningHistoryAlternative(): Flow<History<T>> =
    runningHistory().filterNotNull()

You might need to tweak nullabilities to fit your usecase

You can also replace the History class in with a Pair in case you value brevity over explicitness

甜宝宝 2025-02-14 08:16:22

Flow是顺序的,因此您可以使用变量存储上一个值:

coroutineScope.launch {
    var prevValue = null
    flow.collect { newValue ->
        // use prevValue and newValue here
        ...
        // update prevValue
        prevValue = newValue
    }
}

Flows are sequential, so you can use a variable to store the previous value:

coroutineScope.launch {
    var prevValue = null
    flow.collect { newValue ->
        // use prevValue and newValue here
        ...
        // update prevValue
        prevValue = newValue
    }
}
抽个烟儿 2025-02-14 08:16:22

我认为您可以使用RunningFold非常轻松地做到这一点,因为使用它,您可以使用折叠操作将流动转换为另一个流程。 IS是因为RunningFold确实将所有内容都折叠成一个值,例如fold。取而代之的是,它在作为所有突变的历史上工作之间保持了所有步骤。您唯一需要关注的是,每个返回的值必须是一对,第一个值必须为null,并且第一对必须允许左值中的null。允许null作为蓄能器,还意味着必须将该值滤出。

粗略地,您需要遵循这样的示例:

val initial: Pair<String?, String>? = null
val allPairs = flow {
    emit("Oyster")
    emit("Clam")
    emit("Crab")
}.runningFold(initial) { lastPair, next ->
    lastPair?.run {
        val (_, last) = this
        last to next
    } ?: (null to next)

}.filterNotNull()
allPairs.collect {
    println(it)
}

这样,您就不需要创建运营商类型,因为您的运营商类已经是Pair Pair类。

您可以使用fold等测试差异:

val initial: Pair<String?, String>? = null
val pair = flow {
    emit("Oyster")
    emit("Clam")
    emit("Crab")
}.fold(initial) { lastPair, next ->
    lastPair?.run {
        val (_, last) = this
        last to next
    } ?: (null to next)

}
println(pair)

结帐有关折叠的更多信息以及此处的不同形式:

  1. fold

  2. ://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/running-fold.html“ rel =“ nofollow noreferrer”> runningfold

  3. ​“ https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections” rel =“ nofollow noreferrer”> kotlin.collections.collections

I think you can make this very easily with runningFold, because with it, you can transform a flow into another using a fold operation. The is because runningFold does fold everything into one value like fold. Instead, it keeps all steps in between working as a history of all mutations. The only thing you need to be concerned with is that every returned value must be a pair, the first value must be null and the first pair must allow a null in the left value. Allowing a null as the accumulator means also that this value must be filtered out.

Roughly, you'll need to follow an example like this one:

val initial: Pair<String?, String>? = null
val allPairs = flow {
    emit("Oyster")
    emit("Clam")
    emit("Crab")
}.runningFold(initial) { lastPair, next ->
    lastPair?.run {
        val (_, last) = this
        last to next
    } ?: (null to next)

}.filterNotNull()
allPairs.collect {
    println(it)
}

This way you won't be needing to create a carrier class type, because your carrier class is already the Pair class.

You can test the difference with fold like this:

val initial: Pair<String?, String>? = null
val pair = flow {
    emit("Oyster")
    emit("Clam")
    emit("Crab")
}.fold(initial) { lastPair, next ->
    lastPair?.run {
        val (_, last) = this
        last to next
    } ?: (null to next)

}
println(pair)

Checkout more information about fold and different forms of it here:

  1. fold

  2. runningFold

  3. kotlin.collections

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文