在 Flow 中的 onEach 中过滤集合不起作用
Posted
技术标签:
【中文标题】在 Flow 中的 onEach 中过滤集合不起作用【英文标题】:Filtering collection inside onEach in Flow doesn't work 【发布时间】:2022-01-12 11:32:49 【问题描述】:在特定用例中,我正在调用存储库以获取 Flow 形式的数据。
它的类型是:
Flow<Resource<List<Location>>>
地点:
资源是包装类:
sealed class Resource<T>(val data: T? = null, val message: String? = null)
class Loading<T>(data: T? = null): Resource<T>(data)
class Success<T>(data: T?): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
位置是我的数据模型类
每个位置都有自己的属性,例如类型。当用户切换到类型酒店的部分时,会触发用例方法,进行 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
进行过滤,但最终列表并未更改
更新
存储库调用的返回类型为:
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 ?: ""))
【问题讨论】:
【参考方案1】:如果你想过滤结果,你应该直接过滤它。这里不需要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()
这只是为了解释您可以修改并使其更多kotlinify
。
LocationType
在这里是一个常数,所以你可以直接做这样的事情,你不需要在这里when
。
val result = repository.getLocations()
return if(result.data!=null)
result.data.filter item ->
item.type == locationType
else
emptyList()
要返回 Flow,您可以返回如下所示的内容。
result.map it.data.filter item -> item.type == locationType
【讨论】:
感谢您的承诺,请看帖子更新。 您过滤的方法的返回类型是什么?Flow<Resource<List<Location>>>
【参考方案2】:
filter
不会就地过滤列表。它返回列表的过滤副本。
在您的 Flow 上使用 onEach
也没有任何意义。这只是创建一个新流,它将在收集到每个发出的项目时对其执行onEach
操作。由于您是从这个函数返回的,也许您只需要 Flow 中的第一项,在这种情况下,您应该对其使用 first()
函数,然后使用返回的值。
您需要创建一次过滤副本。然后,您可以将过滤后的副本放回新的 Success
实例以返回。
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)
旁注,您错过了使用密封类的要点。由于您将所有可能的属性都放在父类中,因此将其密封并赋予其子类是没有意义的——它可能只有一个属性来指示它表示加载、成功或错误。现在,即使您检查了子类型,您也必须处理可空的 data
和错误 message
。使用密封类与单个类的全部意义在于避免使它们可以为空。您的父类不应定义任何属性。 Loading 类不需要属性,因此可以是object
。您的 Success 类可以有一个不可为空的 data
属性,而 Error 类可以有一个不可为空的 message
属性。 Success 和 Error 类可以是 data
类,因此它们更容易比较。它应该是这样的:
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>()
【讨论】:
感谢您的回答。现在我明白了 onEach 的作用 ;) 所以如果我想像这样从 Flow 中过滤一些数据,除了先收集这些项目然后再进行一些过滤之外,别无他法?由于存储库调用返回 Flow。 哦,我错过了。我在想List.onEach
。 Flow.onEach
不会像 List.onEach
那样立即迭代 Flow。它返回一个包装原始 Flow 的新 Flow,并在收集新 Flow 时执行 lambda 操作。如果您只需要对 Flow 的第一个值做某事,您可以在其上调用 first()
以获取第一个发出的项目。以上是关于在 Flow 中的 onEach 中过滤集合不起作用的主要内容,如果未能解决你的问题,请参考以下文章
onEach 更改 StateFlow 中的调度程序(kotlin 协程)
使用dwr后,javaweb设置的session超时失效,web.xml和tomcat设置都不起作
我的Android进阶之旅------>Android中ListView中嵌套(ListView)控件时item的点击事件不起作的问题解决方法