过滤两个可选条件的更简单方法

时间:2015-02-02 02:55:09

标签: scala

假设有一个带有两个可选参数的函数代表两种不同的过滤条件。

def judge(a: Option[Int], b: Option[Int]) {
  // here filter according the values of `a` and `b` if are set (not empty)
  // if `a` or `b` is empty, then ignore. 
  // And if `a` and `b` both are empty, then filtering get nothing.
  val objs: List[SomeObject] = ...

  if (!(a.isEmpty) || !(b.isEmpty)) {
    objs.filter { obj =>
      a.map(_ == obj.a).getOrElse(true) && b.map(_ == obj.b).getOrElse(true)
    }
  } else {
    List[SomeObject]()
  }
}

看起来很有效,但我认为方式有点冗长。还有另一种更简单的方法吗?

3 个答案:

答案 0 :(得分:2)

除了过滤器,一切看起来都不错。考虑:

objs filter { obj =>
  ( a.isEmpty || a.get == obj.a ) && ( b.isEmpty || b.get == obj.b )
}

在这些情况下,如果您希望“无”情况下的行为与“某些”案例的行为“混入”,则地图有点无用。

答案 1 :(得分:2)

我会做两处修改。

<强> 1 即可。 !(a.isEmpty) || !(b.isEmpty)很难阅读,需要你(或至少我)仔细检查逻辑,以验证它实际上在做什么。

(a.nonEmpty || b.nonEmpty)会更具表现力和逻辑等效。

<强> 2 即可。我认为在forall中使用filter会更加惯用。

例如:

a.forall(_ == obj.a)

与:

相同
a.map(_ == obj.a).getOrElse(true)

我认为它的作用更清楚。对a中包含的所有元素说,它们必须等于obj.a 。如果a为空,那么默认情况下,它的所有元素都等于obj.a(因为它没有任何元素,所以我们可以对它们说些什么,这将是真的)。​​

现在你的功能看起来更像是这样:

def judge(a: Option[Int], b: Option[Int]): List[SomeObject] = {
  val objs = ...

  if (a.nonEmpty || b.nonEmpty) {
    objs.filter { obj =>
      a.forall(_ == obj.a) && b.forall(_ == obj.b)
    }
  } else Nil
}

答案 2 :(得分:0)

您无需为列表中的每个对象反复测试每个参数是否为Some - 您只需要执行一次。 map执行测试的函数的每个参数,然后将flatten这些参数放入过滤函数列表中。然后,如果有过滤器,reduce将它们放入单个过滤器并将其应用于列表:

def judge(optA: Option[Int], optB: Option[Int]) = {
  val foos = List(Foo(1,2), Foo(3,4))
  val filters = List(
    optA map (a => (foo: Foo) => foo.a == a),
    optB map (b => (foo: Foo) => foo.b == b)
  ).flatten
  if (filters.isEmpty) foos else foos filter filters.reduce{
    (f1, f2) => (foo: Foo) => f1(foo) && f2(foo)
  }
}

使用Vector来保存对象可以通过显着减少分配的对象数量并避免在过滤reverse后完成的隐式List来提高性能。