在 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()
    

这只是为了解释您可以修改并使其更多kotlinifyLocationType 在这里是一个常数,所以你可以直接做这样的事情,你不需要在这里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&lt;Resource&lt;List&lt;Location&gt;&gt;&gt;【参考方案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.onEachFlow.onEach 不会像 List.onEach 那样立即迭代 Flow。它返回一个包装原始 Flow 的新 Flow,并在收集新 Flow 时执行 lambda 操作。如果您只需要对 Flow 的第一个值做某事,您可以在其上调用 first() 以获取第一个发出的项目。

以上是关于在 Flow 中的 onEach 中过滤集合不起作用的主要内容,如果未能解决你的问题,请参考以下文章

onEach 更改 StateFlow 中的调度程序(kotlin 协程)

如何在过滤数据表jQuery中制作多个条件

使用dwr后,javaweb设置的session超时失效,web.xml和tomcat设置都不起作

JSF Flow - 隐式导航不起作用

我的Android进阶之旅------&gt;Android中ListView中嵌套(ListView)控件时item的点击事件不起作的问题解决方法

Dojo dgrid dstore 集合 - “或”过滤器不起作用