2015-02-02 5 views
1

Скажем, что функция с двумя необязательными параметрами представляет собой два разных условия фильтрации.более простой способ фильтрации двух необязательных условий

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]() 
    } 
} 

Кажется, работает, но я думаю, что путь немного подробный. Есть ли еще более простой способ сделать это?

ответ

2

Я бы ввел два изменения.

. !(a.isEmpty) || !(b.isEmpty) трудно читать и требует от вас (или, по крайней мере, меня) более тщательно изучить логику, чтобы проверить, что она на самом деле делает.

(a.nonEmpty || b.nonEmpty) был бы более выразительным и логически эквивалентным.

. Я думаю, было бы более идиоматично использовать 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

Все выглядит хорошо для меня, кроме фильтра. Рассмотрим:

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

В подобных случаях, когда вы хотите, поведение в «None» дело «смешивать в» с поведением для «некоторых» случае, карта вид бесполезно.

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.

Смежные вопросы