Фильтровать ArrayList, используя несколько полей объекта / несколько условий - PullRequest
0 голосов
/ 27 ноября 2018

Итак, у меня есть интересная проблема с фильтрацией коллекций, у меня есть класс данных следующим образом:

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

Я отображаю ArrayList в пользовательском интерфейсе, и у пользователя есть возможность фильтровать список на основеОтносительно того, имеет ли маршрут «недельные / ежемесячные / сезонные» цены, имейте в виду, что я использую флажки, а не переключатели, чтобы пользователь мог фильтровать, используя несколько вариантов.то есть.только еженедельно, или еженедельно, и ежемесячно и т. д.

Моя проблема в том, что с помощью функции фильтра и предиката я могу фильтровать только по одному атрибуту за раз, например:

routes.filter {
    it.weeklyPrice != null
}

Но иногда я фактически отфильтровал бы значения, которые хотел бы сохранить, основываясь на другом условии, например, пользователю не нужны маршруты с недельными ценами, поэтому я отфильтровываю их, но ему нужны месячные, но некоторые из них былиуже отфильтрованы на основании того факта, что у них также были недельные цены.

Любая помощь будет оценена, я уверен, что это можно сделать, я просто не понял, как, может быть, проблема в том, какданные представлены в первую очередь?Я не уверен.

Большое спасибо заранее.

Ответы [ 3 ]

0 голосов
/ 27 ноября 2018

Один из подходов состоит в том, чтобы создать список фильтров, например, 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())
0 голосов
/ 28 ноября 2018

Хорошо, вот что я в итоге сделал ... что я делал до того, как увидел ответ @ 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)
}

Помните, что ответ, который я пометил как правильный (не этот), вероятно, является лучшим способом сделать это.

0 голосов
/ 27 ноября 2018

Если вы хотите сохранить значения, кэшируйте все маршруты и затем отфильтруйте их:

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