在 Flow 中的 onEach 中过滤集合不起作用

Filtering collection inside onEach in Flow doesn't work

在特定用例中,我正在调用存储库以获取 Flow 形式的数据。

它的类型是:

Flow<Resource<List<Location>>>

其中:

每个位置都有自己的 属性 类型。当用户切换到酒店类型的部分时,触发用例方法,api 进行调用,我正在过滤列表,以便它只包含所需的项目。

但是问题是过滤机制不起作用。

return repository.getLocations()
        .onEach { result ->
            if (result.data != null) {
                when (locationType) {
                    is LocationType.All -> result.data
                    is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
                    is LocationType.Explore -> result.data.filter { it.type == "Explore" }
                    is LocationType.Active -> result.data.filter { it.type == "Active" }
                    is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }
                }   
            }
        }

尽管使用onEach

进行过滤,但最终列表没有改变

更新

存储库调用的 return 类型是:

Flow<Resource<List<Location>>>

解决方案

我终于想出了解决办法。 在我的用例中,我正在收集流程并在 collect lambda 内部,我已经放置了负责过滤的代码。 当一切都完成后,我只是进一步发射数据 ;)

operator fun invoke(
    locationType: LocationType
) = flow {
     repository.getLocations().collect { result ->
        if (result.data != null) {
            when (locationType) {
                is LocationType.All -> result.data
                is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
                is LocationType.Explore -> result.data.filter { it.type == "Explore" }
                is LocationType.Active -> result.data.filter { it.type == "Active" }
                is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }.also { res ->
                when (result) {
                    is Resource.Success -> {
                        emit(Resource.Success(data = res))   }
                    is Resource.Loading -> {
                        emit(Resource.Loading(data = res))
                    }
                    is Resource.Error -> {
                        emit(Resource.Error(data = res, message = result.message ?: ""))
                    }
                }
            }

如果你想过滤结果你应该直接过滤它。这里不需要 onEach。 你可以这样做。

val result = repository.getLocations()
    return if(result.data!=null){
        result.data.filter { item ->
            when (locationType) {
                is LocationType.Hotel -> item.type == "Hotel"
                is LocationType.Explore -> item.type == "Explore"
                is LocationType.Active -> item.type == "Active"
                is LocationType.Restaurant ->item.type == "Restaurant"
                else -> true
            }
        }
    }else{
        emptyList()
    }

这只是为了解释,您可以修改并使其更多kotlinifyLocationType 在这里是一个常数,所以你可以直接做这样的事情你不需要 when 在这里。

val result = repository.getLocations()
    return if(result.data!=null){
        result.data.filter { item ->
                item.type == locationType
        }
    }else{
        emptyList()
    }

要 return 流,您可以 return 如下所示。

result.map { it.data.filter { item -> item.type == locationType } }

filter 不会就地过滤列表。它 return 是列表的过滤 副本

在 Flow 上使用 onEach 也没有意义。这只是创建一个新流程,它将在收集每个发出的项目时对其执行 onEach 操作。由于您是从该函数中 returning 的,也许您只需要 Flow 中的第一项,在这种情况下,您应该对其使用 first() 函数,然后使用 returned值。

您需要创建一次过滤副本。然后你可以把过滤后的副本放回一个新的 Success 实例到 return.

val result repository.getLocations().first()
if (result !is Success<List<Location>>) {
    return result
}
val filteredData = result.data?.filter {
    it.type == when (locationType) {
        is LocationType.All -> it.type
        is LocationType.Hotel -> "Hotel"
        is LocationType.Explore -> "Explore"
        is LocationType.Active -> "Active"
        is LocationType.Restaurant -> "Restaurant"
    }
}
return Success(data = filteredData)

旁注,您忽略了使用密封 class 的要点。由于您将所有可能的属性都放在 parent class 中,因此将其密封并赋予它 children 是没有意义的——它可以再有一个 属性指示它代表加载、成功或错误。现在您必须处理可空 data 和错误 message,即使您已经检查了 child 类型。使用密封 class 与单个 class 的全部意义在于避免必须使这些可为空。您的 parent class 不应定义任何属性。 Loading class 不需要属性,因此可以是 object。您的成功 class 可以有一个 non-nullable data 属性,而错误 class 可以有一个 non-nullable message 属性。 Success 和 Error classes 可以是 data classes 以便更容易比较它们。它应该是这样的:

sealed class Resource<T> {
    object Loading<T>: Resource<T>()
    data class Success<T>(val data: T): Resource<T>()
    data class Error<T>(message: String): Resource<T>()
}