使用多个对象字段/多个条件过滤ArrayList

时间:2018-11-27 15:11:03

标签: collections filter kotlin

所以我在过滤集合时遇到了一个有趣的问题,我有一个如下的数据类:

data class Route(
    val id: Int,
    val name: String,
    val weeklyPrice: Double?,
    val monthlyPrice: Double?,
    val seasonalPrice: Double?,
)

我在UI中显示ArrayList,用户可以根据路线是否具有“每周/每月/季节性”价格过滤列表,请记住我使用的是复选框而不是单选按钮,因此用户可以使用多个选项进行过滤。即。仅每周一次,或者每周和每月一次,等等。

我的问题是,使用过滤器功能和谓词,我一次只能基于一个属性进行过滤,

routes.filter {
    it.weeklyPrice != null
}

但是有时候我实际上会根据其他条件过滤掉我想保留的值,例如,某个用户不希望使用每周价格的路线,因此我将其过滤掉,但他想要使用每月价格的那些,但其中一些已基于它们也具有每周价格的事实而被过滤掉。

任何帮助将不胜感激,我确定可以做到,但我还没有弄清楚如何做,也许问题出在首先是如何表示数据?我不确定。

非常感谢。

3 个答案:

答案 0 :(得分:1)

一种方法是生成过滤器列表,例如List<(Route) -> Boolean>,然后只要过滤器列表更改,就使视图无效。

例如,您有类似的东西:

// Use your own logic based on user selection here
private fun generateFilters() = listOf<(Route) -> Boolean>(
    { it.id != 0 },
    { it.weeklyPrice != null }
)

然后,您的视图是基于这组过滤器生成的,即:

private fun filterRoutes(routes: List<Route>, filters: List<(Route) -> Boolean>) =
    routes.filter { route -> filters.all { filter -> filter(route) } 

您甚至可以在List<T>上添加扩展功能以提供filterAll功能,例如:

private fun <T> List<T>.filterAll(filters: List<(T) -> Boolean>) =
    filter { item -> filters.all { filter -> filter(item) } }

然后您的视图逻辑就变成:

routers.filterAll(generateFilters())

答案 1 :(得分:0)

如果要保留值,请缓存所有路由,然后对其进行过滤:

val allRoutes = ... // somewhere initialized
val filtered = allRoutes.filter {
    it.monthlyPrice != null
}
// use filtered routes, but keep reference to allRoutes

答案 2 :(得分:0)

好吧,这就是我最终要做的..在看到上面@kcoppock的答案之前,我所做的。

我使用了MutableSet来保存我要主动显示给用户的所有路由,并使用以下代码侦听每个框的所有CheckedChanged事件:

view.cb_weekly.setOnCheckedChangeListener { _, isChecked ->
    weeklyIncluded = isChecked
    if (weeklyIncluded) {
        activeRoutes.addAll(routes.filter { route ->
            route.weeklyPrice != null
        })
    } else {
        activeRoutes.removeAll(routes.filter { route ->
            route.weeklyPrice != null && route.monthlyPrice == null && route.seasonalPrice == null
        })
        if (!monthlyIncluded && seasonalIncluded) {
            activeRoutes.removeAll(routes.filter { route ->
                (route.weeklyPrice != null || route.monthlyPrice != null) && route.seasonalPrice == null
            })
        } else if (!seasonalIncluded && monthlyIncluded) {
            activeRoutes.removeAll(routes.filter { route ->
                (route.weeklyPrice != null || route.seasonalPrice != null) && route.monthlyPrice == null
            })
        } else if (!seasonalIncluded && !monthlyIncluded) {
            activeRoutes.clear()
        }
    }
    drawRoutes(activeRoutes)
}

请记住,我标记为正确答案(不是这个答案)可能是执行此操作的更好方法。