【问题标题】:How to filter a collection using a list of predicates如何使用谓词列表过滤集合
【发布时间】:2021-09-13 22:37:49
【问题描述】:

我有一个元素列表,例如:

val myList = listOf(1,2,3,4,5,6,7)

列表可以是任何类型,这只是一个示例。 现在我有一个任意长度的谓词列表:

val myPredicates = listOf({myInt: Int -> myInt > 1}, {myInt: Int -> myInt%2 == 0})

如何通过按列表顺序将所有谓词应用于myList 来过滤列表并将结果放入新列表中?我觉得答案就在使用reducefold 运算符的某个地方,但答案却在我面前。

【问题讨论】:

  • 明确一点:您只想保留与所有谓词匹配的项目?
  • 正确。就像这样链接谓词:myList.filter(predicate1).filter(predicate2),但你不知道谓词的数量提前。

标签: kotlin collections filtering


【解决方案1】:

我能想到的最简单的解决方案是在您的 filter 调用中的谓词列表中使用 all,如下所示:

myList.filter { elt -> myPredicates.all { it(elt) } }

当给定元素elt 的所有谓词都为真时,myPredicates.all { it(elt) } 部分返回真。

【讨论】:

  • 哇,看似简单。我为自己没有弄清楚这一点而感到愚蠢。谢谢。
  • 哈哈不用担心,很容易错过简单的事情:)
【解决方案2】:

一种方法是首先使用fold 将所有谓词组合成一个谓词((T) -> Boolean),然后使用内置的filter 方法应用该谓词。

fun <T> Iterable<T>.filter(predicates: Iterable<(T) -> Boolean>) =
    predicates.fold({ true }) { acc: (T) -> Boolean, next ->
        { acc(it) && next(it) }
    }.let { this.filter(it) }

fun main() {
    val myList = listOf(1,2,3,4,5,6,7)
    val myPredicates = listOf({myInt: Int -> myInt > 2}, {myInt: Int -> myInt%2 == 0})
    println(myList.filter(myPredicates))
    // [4, 6]
}

由于&amp;&amp; 的短路特性以及acc(it) 后跟next(it) 的顺序,每个谓词将按顺序运行,如果任何一个产生false,其余的都不会运行.

【讨论】:

  • 与仅评估常规 filter 中的所有谓词相比,这不是有点做作吗?这种方法有什么好处?我想必须在那里创建一堆捕获 lambda,所以我不确定它是否超过了幼稚方法的迭代器创建。
  • @Joffrey 我看到了提到fold 的问题并得到了洞见。你的答案显然更好!
猜你喜欢
  • 2014-10-26
  • 1970-01-01
  • 2020-05-20
  • 2010-09-12
  • 1970-01-01
  • 2019-03-17
  • 2012-04-10
  • 1970-01-01
相关资源
最近更新 更多