4

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)?

1

3 Answers 3

4

You can use withFilter:

list.withFilter(nameFilter("Apples")).withFilter(lengthFilter(5))... 
Sign up to request clarification or add additional context in comments.

2 Comments

ooo... So withFilter(filter) chains them together without creating a new list for each filter?
Looking at withFilter implementation code now, my outlook on Scala keeps getting better! Thanks for the help :D
1

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

7 Comments

It does not look like the filters can be added dynamically here. Assuming dynamic addition is not a requirement, why not simply create a nameAndLengthFilter(s: String, i: Int): (String) => Boolean?
@BAR Updated with dynamic predicate append example
I am using filters dynamically by adding them if the if statement that wraps them is true. So I still could not use your example dynamically enough with as concise of code as the withFilter approach. Your answer did show me some interesting points so +1 for you.
I realized your Predicate code is basically the implementation of withFilter in TraversableLike github.com/scala/scala/blob/v2.10.3/src/library/scala/…
"I still could not use your example dynamically enough" Sure you can dynamically add to a Predicate as you can to a WithFilter. However, for your particular use case where you only need && filter chaining, there is no doubt that the library supported method is just better to use. It would be interesting to add additional filter chaining methods like || or ! to the WithFilter class instead, but my preliminary attempts failed due to access modifiers.
|
1

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.

2 Comments

This is closely related to withFilter. I have been studying its implementation code, thanks for pointing it out.
After further comparing the withFilter and view implementations I have found that although view does not create any lists, it does have to traverse twice, once for each filter, albeit on a limited view after the first filter. While using withFilter allows for the list to be traversed once by checking for both filter conditions simultaneously.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.