【问题标题】:Conditionally Combine Lambdas in Kotlin for Filtering with Multiple Predicates有条件地在 Kotlin 中组合 Lambda 以过滤多个谓词
【发布时间】: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&lt;Predicate&lt;T&gt;&gt; 并将谓词与 .and() 或 &amp;&amp; 组合,以便我可以应用过滤器 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 的结果与&amp;&amp; 相结合以进行交集?
  • @AnimeshSahu 我只想在满足条件时应用某些 lambda。每个 lambda 的条件是独立的,我有超过 2 个(在我的示例中为 5 个),否则我将列出谓词的每个组合情况。比如if (a) applyPredicateA() AND if (b) applyPredicateB() AND if(c) 等等。
  • 你能指出任何例子,输入和他们的预期输出,我没有得到你想要做的。
  • list.filter { conditions.all { pred -&gt; pred.test(it) } } ?
  • 使用 lambdas 代替 Predicate 接口,ArrayList&lt;(Villager)-&gt;Boolean&gt;

标签: android kotlin filter predicate


【解决方案1】:

有all 扩展函数,作为兴趣点还有any 扩展函数。我认为您只需要在谓词的过滤器块中使用它们即可。这是一个非常简单的示例,其中包含一组愚蠢的谓词:

fun main() {
    val thingsToFilter = listOf(-1,2,5)

    val predicates = listOf(
        { value: Int -> value > 0 },
        { value: Int -> value > 4 }
    )

    val filtered = thingsToFilter.filter {candidate ->
        predicates.all{ it(candidate)}
    }

    print(filtered) // Outputs [5]

}

【讨论】:

    【解决方案2】:

    根据 Laurence 的回答,您可以创建这样的扩展程序

    inline fun <T> Iterable<T>.filterAll(vararg predicates: (T) -> Boolean): List<T> {
        return filter { candidate ->
            predicates.all { it(candidate) }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多