Kotlin协同程序流中RxJava.toList()的等价物

Kotlin协同程序流中RxJava.toList()的等价物,kotlin,rx-java,kotlin-coroutines,kotlin-flow,Kotlin,Rx Java,Kotlin Coroutines,Kotlin Flow,我需要观察用户ID,然后使用这些用户ID来观察用户。用户ID或用户可以随时更改,我希望使发出的用户保持最新。 以下是我拥有的数据来源示例: data class User(val name: String) fun observeBestUserIds(): Flow<List<String>> { return flow { emit(listOf("abc", "def")) delay(500) emit(

我需要观察用户ID,然后使用这些用户ID来观察用户。用户ID或用户可以随时更改,我希望使发出的用户保持最新。 以下是我拥有的数据来源示例:


data class User(val name: String)

fun observeBestUserIds(): Flow<List<String>> {
    return flow {
        emit(listOf("abc", "def"))
        delay(500)
        emit(listOf("123", "234"))
    }
}

fun observeUserForId(userId: String): Flow<User> {
    return flow {
        emit(User("${userId}_name"))
        delay(2000)
        emit(User("${userId}_name_updated"))
    }
}
我将编写什么函数来生成一个发出这种信息的流?

您可以使用


不幸的是,对于kotlin流的当前状态来说,这是非常重要的,似乎缺少了一些重要的操作符。但是请注意,您并不是在寻找rxJavas toList()。如果您试图使用rxjava中的
toList
concatMap
来执行此操作,则必须等待所有observabe完成。 这不是你想要的

不幸的是,我认为没有办法绕过自定义函数

它必须将
observeurforid
返回的所有结果聚合为您将传递给它的所有ID。它也不是一个简单的窗口函数,因为实际上可以想象一个
observeurforid
已经返回了两次,而另一个调用仍然没有完成。因此,在向聚合函数传递id时,检查是否已经拥有相同数量的用户是不够的,还必须按用户id分组

今天晚些时候我将尝试添加代码

编辑:正如我的解决方案所承诺的,我冒昧地稍微增加了需求。因此,每当所有userid都有值并且底层用户发生更改时,流就会发出消息。我认为这更可能是您想要的,因为用户可能不会在lockstep中更改属性

然而,如果这不是你想要的,请留下评论

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking


data class User(val name: String)

fun observeBestUserIds(): Flow<List<String>> {
    return flow {
        emit(listOf("abc", "def"))
        delay(500)
        emit(listOf("123", "234"))
    }
}

fun observeUserForId(userId: String): Flow<User> {
    return flow {
        emit(User("${userId}_name"))
        delay(2000)
        emit(User("${userId}_name_updated"))
    }
}

inline fun <reified K, V> buildMap(keys: Set<K>, crossinline valueFunc: (K) -> Flow<V>): Flow<Map<K, V>> = flow {
    val keysSize = keys.size
    val valuesMap = HashMap<K, V>(keys.size)
    flowOf(*keys.toTypedArray())
            .flatMapMerge { key -> valueFunc(key).map {v -> Pair(key, v)} }
            .collect { (key, value) ->
                valuesMap[key] = value
                if (valuesMap.keys.size == keysSize) {
                    emit(valuesMap.toMap())
                }
            }
}

fun observeUsersForIds(): Flow<List<User>> {
    return observeBestUserIds().flatMapLatest { ids -> buildMap(ids.toSet(), ::observeUserForId as (String) -> Flow<User>) }
            .map { m -> m.values.toList() }
}


fun main() = runBlocking {
    observeUsersForIds()
        .collect { user ->
            println(user)
        }
}

您可以在线运行代码

我相信您正在寻找的是
组合
,它为您提供了一个数组,您可以轻松调用
toList()

observebestUserId().collectLatest{id->
结合(
ids.map{id->observeUserForId(id)}
) {
it.toList()
}.收集{
println(it)
} 
}
下面是内部部分,其中包含更明确的参数名称,因为您看不到IDE的类型暗示堆栈溢出:

联合收割机(
ids.map{id->observeUserForId(id)}
){ArrayFuser:数组->
arrayOfUsers.toList()
}.收集{listofuser:List->
println(列表用户)
}
输出:

[User(name=abc_name), User(name=def_name)]
[User(name=123_name), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name_updated)]
(请注意,在演示中,所有输出都会一次显示,但这是演示站点的一个限制-当代码在本地运行时,行的显示时间与您期望的时间一致)

这避免了原始问题中讨论的(
abc_name_updated
def_name_updated
)。但是,仍然有一个中间版本的
123\u name\u updated
234\u name
,因为
123\u name\u updated
是首先发出的,它会立即发送组合版本,因为它们是每个流中的最新版本

但是,这可以通过消除排放的抖动来避免(在我的机器上,只有1ms的超时有效,但为了保守起见,我做了20ms):

observebestUserId().collectLatest{id->
结合(
ids.map{id->observeUserForId(id)}
) {
it.toList()
}.debounce(timeoutMillis=20)。收集{
println(it)
}
}
这将为您提供所需的确切输出:

[User(name=abc_name), User(name=def_name)]
[User(name=123_name), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name_updated)]

您是想要列出3种排放物的流量,还是想要与标题中的
toList
相同的流量?它们是完全不同的问题。我只想要在任何给定时间的最新用户,所以列出的排放量是可以的。您是否在排放量2和3之间缺少
[用户(abc\u名称更新),用户(def\u名称更新)]
,或者他们是否应该被排放(如果是,为什么)?我不介意,这也可能被排放,但是想象延迟反映了来自数据库的排放,我并不真正关心这个排放,因为ID123和234应该已经被排放,而abc和def已经过时了,这会导致类型不匹配<代码>id是字符串列表。更新了响应请提供帮助。我想我们越来越近了。但是,它返回一个
。我想要的是一个
流量
,排放量如上所述。再次更新,抱歉花了这么长时间。这提供了一个
流量
。我想要一份用户名单。看起来很简单,但我就是搞不懂。非常好,谢谢。似乎Flow缺少了一个像combine这样的操作符,它等待所有流至少发出一次,而不是使用debounce操作符,这在我看来有点麻烦。Zip可用,但仅用于2个流,而不是流列表。事实上,它确实等待所有流至少发出一次-这可以通过添加
if(userId==“def”)来证明延迟(250)
observeUserForId
——但正如您所注意到的,它不会像zip操作员那样在随后的每次等待匹配的排放。基于这个问题,我认为当前的行为可能是正确的:因为您正在观察用户的更改,所以您不希望在显示任何更新之前等待您正在监视的每个用户至少更新一次他们的信息,而是希望在更新发生时立即显示更新。但在某些情况下,zip可能也是正确的。啊,你是对的。这是正确的,没有去盎司。出色的工作,谢谢!
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking


data class User(val name: String)

fun observeBestUserIds(): Flow<List<String>> {
    return flow {
        emit(listOf("abc", "def"))
        delay(500)
        emit(listOf("123", "234"))
    }
}

fun observeUserForId(userId: String): Flow<User> {
    return flow {
        emit(User("${userId}_name"))
        delay(2000)
        emit(User("${userId}_name_updated"))
    }
}

inline fun <reified K, V> buildMap(keys: Set<K>, crossinline valueFunc: (K) -> Flow<V>): Flow<Map<K, V>> = flow {
    val keysSize = keys.size
    val valuesMap = HashMap<K, V>(keys.size)
    flowOf(*keys.toTypedArray())
            .flatMapMerge { key -> valueFunc(key).map {v -> Pair(key, v)} }
            .collect { (key, value) ->
                valuesMap[key] = value
                if (valuesMap.keys.size == keysSize) {
                    emit(valuesMap.toMap())
                }
            }
}

fun observeUsersForIds(): Flow<List<User>> {
    return observeBestUserIds().flatMapLatest { ids -> buildMap(ids.toSet(), ::observeUserForId as (String) -> Flow<User>) }
            .map { m -> m.values.toList() }
}


fun main() = runBlocking {
    observeUsersForIds()
        .collect { user ->
            println(user)
        }
}
[User(name=def_name), User(name=abc_name)]
[User(name=123_name), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name_updated)]
[User(name=abc_name), User(name=def_name)]
[User(name=123_name), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name_updated)]
[User(name=abc_name), User(name=def_name)]
[User(name=123_name), User(name=234_name)]
[User(name=123_name_updated), User(name=234_name_updated)]