【发布时间】:2020-09-20 11:41:27
【问题描述】:
假设我有以下谓词作为 Villager POJO 的预定义 lambda。
val matchesSearch: (Villager, String) -> Boolean =
{ villager: Villager, query: String -> villager.name.contains(query) }
val matchesGender: (Villager, Int) -> Boolean =
{ villager: Villager, filter: Int -> filter == villager.gender }
val matchesPersonality: (Villager, Int) -> Boolean =
{ villager: Villager, filter: Int -> filter == villager.personality }
val matchesSpecies: (Villager, Int) -> Boolean =
{ villager: Villager, filter: Int -> filter == villager.species }
val matchesHobby: (Villager, Int) -> Boolean =
{ villager: Villager, filter: Int -> filter == villager.hobby }
我只想在满足某个条件时应用谓词。例如,如果已应用性别过滤器,则仅按性别过滤。或者我只想匹配已输入的搜索查询。
fun getVillagersThatMatchQueryAndFilters(list: List<Villager>): List<Villager> {
val conditions = ArrayList<Predicate<Villager>>()
if (searchQuery.isNotEmpty()) {
// TODO: apply the matchesSearch() predicate lambda
conditions.add(Predicate { villager -> villager.name.contains(query) })
}
if (genderFilter > 0) {
// TODO: apply the matchesGender() predicate lambda
conditions.add(Predicate { genderFilter == villager.gender })
}
if (personalityFilter > 0) {
// TODO: apply the matchesPersonality() predicate lambda
conditions.add(Predicate { personalityFilter == villager.personality })
}
if (speciesFilter > 0) {
// TODO: apply the matchesSpecies() predicate lambda
conditions.add(Predicate { speciesFilter == villager.species })
}
if (hobbyFilter > 0) {
// TODO: apply the matchesHobby() predicate lambda
conditions.add(Predicate { hobbyFilter == villager.hobby })
}
return list.filter {
// TODO: match on all conditionally applied predicates
conditions.allPredicatesCombinedWithAnd() // this doesn't actually exist
}
}
以前我在每个条件中都完成了list.filter{ } 并应用了必要的谓词,但是,由于我最多可以应用 5 个谓词,所以性能非常糟糕,因为它每次 .filter() 时都会遍历列表调用。
有没有办法以编程方式迭代 List<Predicate<T>> 并将谓词与 .and() 或 && 组合,以便我可以应用过滤器 ONCE?或者如果不是,我如何有条件地组合这些谓词?
我会使用最终的 Java 示例 here,它使用了 Predicates.stream() 和 Collectors,但它需要 Android API 级别 24(高于我当前的 Android 最低要求)。
以下解决方案 - 感谢@Laurence 和@IR42 的建议
适用于
创建一个 lambda 列表并有条件地添加您希望匹配列表项的任何谓词。然后使用Collections.Aggregates.all() 方法确保您正在过滤所有谓词。 (相当于对列表中的所有项目执行pred1.and(pred2).and(etc.)...。)
如果您有大量要过滤的谓词,因为 list.filter() 只被称为 ONCE。
fun getVillagersThatMatchQueryAndFilters(list: List<Villager>): List<Villager> {
val conditions = ArrayList<(Villager) -> Boolean>()
if (searchQuery.isNotEmpty()) {
conditions.add{ it.name.contains(query) }
}
if (genderFilter > 0) {
conditions.add{ genderFilter == it.gender }
}
if (personalityFilter > 0) {
conditions.add{ personalityFilter == it.personality }
}
if (speciesFilter > 0) {
conditions.add{ speciesFilter == it.species }
}
if (hobbyFilter > 0) {
conditions.add{ hobbyFilter == it.hobby }
}
return list.filter { candidate -> conditions.all { it(candidate) } }
}
【问题讨论】:
-
创建一个新的 lambda 有什么问题,将两个 lambda 的结果与
&&相结合以进行交集? -
@AnimeshSahu 我只想在满足条件时应用某些 lambda。每个 lambda 的条件是独立的,我有超过 2 个(在我的示例中为 5 个),否则我将列出谓词的每个组合情况。比如
if (a) applyPredicateA()ANDif (b) applyPredicateB()ANDif(c)等等。 -
你能指出任何例子,输入和他们的预期输出,我没有得到你想要做的。
-
list.filter { conditions.all { pred -> pred.test(it) } }? -
使用 lambdas 代替
Predicate接口,ArrayList<(Villager)->Boolean>
标签: android kotlin filter predicate