scala,根据几个条件过滤集合

时间:2013-07-01 14:06:17

标签: scala

我有一个代码如:

val strs = List("hello", "andorra", "trab", "world")
def f1(s: String) = !s.startsWith("a")
def f2(s: String) = !s.endsWith("b")

val result = strs.filter(f1).filter(f2)

现在,应根据条件应用f1和f2,例如:

val tmp1 = if (cond1) strs.filter(f1) else strs
val out  = if (cond2) tmp1.filter(f2) else tmp1

有没有更好的方法可以做到这一点,而不使用临时变量tmp1

一种方法是根据功能列表进行过滤,例如:

val fs = List(f1 _,f2 _)
fs.foldLeft(strs)((fn, list) => list.filter(fn))

但是我需要根据条件构建一个函数列表(因此,我会将使用临时字符串列表变量的问题转移到使用临时函数列表变量(或者我应该使用可变列表))。

我看起来像这样(当然这不会编译,否则我已经有了问题的答案):

val result = 
  strs
    .if(cond1, filter(f1))
    .if(cond2, filter(f2))

4 个答案:

答案 0 :(得分:8)

您可以轻松使用隐式类来为您提供以下语法:

  val strs = List("hello", "andorra", "trab", "world")

  def f1(s: String) = !s.startsWith("a")

  def f2(s: String) = !s.endsWith("b")

  val cond1 = true
  val cond2 = true

  implicit class FilterHelper[A](l: List[A]) {
    def ifFilter(cond: Boolean, f: A => Boolean) = {
      if (cond) l.filter(f) else l
    }
  }

  strs
    .ifFilter(cond1, f1)
    .ifFilter(cond2, f2)

res1: List[String] = List(hello, world)

我会使用if作为方法名称,但它是一个保留字。

答案 1 :(得分:3)

您可以通过对谓词函数求和来完成此操作。

观察过滤谓词A => Boolean是否有附加操作:

def append[A](p1: A => Boolean, p2: A => Boolean): A => Boolean =
  a => p1(a) && p2(a)

身份值:

def id[A]: A => Boolean =
  _ => true

满足任何谓词p: A => Booleanappend(p, id) === p

的条件

这简化了基于条件包含/排除谓词的问题:如果条件为假,则只需包含id谓词。它对过滤器没有影响,因为它总是返回true

对谓词求和:

def sum[A](ps: List[A => Boolean]): A => Boolean =
  ps.foldLeft[A => Boolean](id)(append)

请注意,我们会折叠到id,因此如果ps为空,我们会获得身份谓词,即一个不做任何事情的过滤器,正如您所期望的那样。

把这一切放在一起:

val predicates = List(cond1 -> f1 _, cond2 -> f2 _)

strs.filter(sum(predicates.collect { case (cond, p) if cond => p }))
// List(hello, world)

请注意,列表strs仅被遍历一次。


现在,对于上面的Scalaz版本:

val predicates = List(cond1 -> f1 _, cond2 -> f2 _)

strs filter predicates.foldMap {
  case (cond, p) => cond ?? (p andThen (_.conjunction))
}
// List("hello", "world")

答案 2 :(得分:3)

@Noah的回答是好的,如果你想能够在列表上执行任何类型的操作然后在给出以下更改的情况下返回一个新的List,你可以接受它并进一步概括:

implicit class FilterHelper[A](l: List[A]) {
  def ifthen[B](cond: Boolean, f:(List[A]) => List[B]) = {
    if (cond) f(l) else l
  }
}

然后像这样使用它:

val list = List("1", "2")    
val l2 = list.ifthen(someCondition, _.filter(f1)
val l3 = list.ifthen(someOtherCondition, _.map(_.size))

答案 3 :(得分:2)

在过滤器的块中包含条件会非常简单,如下所示:

val result = strs filter (x => !cond1 || f1(x)) filter (x => !cond2 || f2(x))

如果满足条件,结果将应用过滤器,或者只返回相同的列表。