Reputation: 17151
I want to chain a bunch of filters but do not want the overhead associated with creating multiple lists.
type StringFilter = (String) => Boolean
def nameFilter(value: String): StringFilter =
(s: String) => s == value
def lengthFilter(length: Int): StringFilter =
(s: String) => s.length == length
val list = List("Apple", "Orange")
Problem is this builds a list after each filter:
list.filter(nameFilter("Apples")).filter(lengthFilter(5))
// list of string -> list of name filtered string -> list of name and length filtered string
I want:
// list of string -> list of name and length filtered string
I find out which filters are needed at run-time so I must add filters dynamically.
// Not sure how to implement add function.
val filterPipe: StringFilter = ???
// My preferred DSL (or very close to it)
filterPipe.add(nameFilter("Apples")
filterPipe.add(lengthFilter(5))
// Must have DSL
list.filter(filterPipe)
How can I implement filterPipe
?
Is there some way to recursively AND the filter conditions together in a filterPipe (which is itself a StringFilter)?
Upvotes: 4
Views: 1960
Reputation: 20425
Consider also a view on the filters, like this,
list.view.filter(nameFilter("Apples")).filter(lengthFilter(5))
This prevents intermediate collections, namely for each entry in list
it applies the subsequent filters.
Upvotes: 1
Reputation: 2169
A blog post suggest another alternative using an implicit class to allow aggregating multiple predicates using custom operators
implicit class Predicate[A](val pred: A => Boolean) {
def apply(x: A) = pred(x)
def &&(that: A => Boolean) = new Predicate[A](x => pred(x) && that(x))
def ||(that: A => Boolean) = new Predicate[A](x => pred(x) || that(x))
def unary_! = new Predicate[A](x => !pred(x))
}
Then you can apply the predicate chain as follows
list.filter { (nameFilter("Apple") && lengthFilter(5)) (_) }
You can also chain the predicates dynamically
val list = List("Apple", "Orange", "Meat")
val isFruit = nameFilter("Apple") || nameFilter("Orange")
val isShort = lengthFilter(5)
list.filter { (isFruit && isShort) (_) }
As you can see the benefit of this approach compared to the withFilter
approach is that you can combine the predicates arbitrarily
Upvotes: 1
Reputation: 144206
You can use withFilter
:
list.withFilter(nameFilter("Apples")).withFilter(lengthFilter(5))...
Upvotes: 4