Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filtering a List based on Optional Parameter Values

Tags:

scala

Is there a more elegant way of filtering a list based on optional parameter values?

def f(dates: List[Date], start: Option[Long], end: Option[Long]): List[Date] = {
    (start, end) match {
        case (Some(s), Some(e)) =>  dates filter (_.getTime > s) filter (_.getTime < e)
        case (Some(s), None) => dates filter (_.getTime > s)
        case (None, Some(e)) => dates filter (_.getTime < e)
        case (None, None) => dates
    }
}

With three optional parameter values this would have 9 cases etc.

like image 579
z12345 Avatar asked Oct 22 '13 12:10

z12345


3 Answers

One way would be as follows:

def f(dates: List[Date], start: Option[Long], end: Option[Long]): List[Date] =
  dates.filter( d => start.map(d.getTime > _).getOrElse(true) )
       .filter( d => end.map(d.getTime < _).getOrElse(true) )

or, even more succinct, you could use forall on the options:

def f(dates: List[Date], start: Option[Long], end: Option[Long]): List[Date] =
  dates.filter( d => start.forall(d.getTime > _) )
       .filter( d => end.forall(d.getTime < _) )
like image 113
Shadowlands Avatar answered Nov 28 '22 09:11

Shadowlands


In the case of an arbitrary amount of filters:

You could first change the parameter to List[Option[Date => Boolean]]. Then combine all filters that are actually there. Then apply the combined filter.

def f(dates : List[Date], filters : List[Option[Date => Boolean]]) = {
    val combFilter = filters.foldLeft((d : Date) => true)((comb, filter) => if(filter.isDefined) (d : Date) => comb(d) && filter.get(d) else comb)
    dates.filter(combFilter)
}

And assuming you have dates, start and end, you can call it like:

f(dates, List(start.map(s => _.getTime > s), end.map(e => _.getTime < e))
like image 32
Kigyo Avatar answered Nov 28 '22 09:11

Kigyo


I think the key in your question is how to transform the parameters provided into a meaningful condition. Then you can scale up that method to any number of parameters (filtering conditions).

Using an intrinsic conversion (so, your code knows what to do with the parameter), I would approach like this:

def f(dates: List[Date], start: Option[Long], end: Option[Long]): List[Date] = {
      val filters = List(start.map(x=>{y:Long=>y>x}), end.map(x=>{y:Long=>y<x})).flatten
      dates.flatMap(date => if (filters.forall(filter =>  filter(date.getTime))) Some(date) else None)
  }
like image 45
maasg Avatar answered Nov 28 '22 09:11

maasg