BAR
BAR

Reputation: 17151

Chain Scala Filters Without the Overhead

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

Answers (3)

elm
elm

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

mucaho
mucaho

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

Lee
Lee

Reputation: 144206

You can use withFilter:

list.withFilter(nameFilter("Apples")).withFilter(lengthFilter(5))...

Upvotes: 4

Related Questions