7

有没有更优雅的方法来根据可选参数值过滤列表?

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
    }
}

使用三个可选参数值,这将有 9 种情况等。

4

6 回答 6

9

一种方法如下:

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

或者,更简洁,您可以forall在选项上使用:

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 < _) )
于 2013-10-22T13:01:35.003 回答
5

在任意数量的过滤器的情况下:

您可以先将参数更改为 List[Option[Date => Boolean]]。然后结合所有实际存在的过滤器。然后应用组合过滤器。

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

假设你有日期、开始和结束,你可以这样称呼它:

f(dates, List(start.map(s => _.getTime > s), end.map(e => _.getTime < e))
于 2013-10-22T13:42:37.237 回答
2

我认为您问题的关键是如何将提供的参数转换为有意义的条件。然后,您可以将该方法扩展到任意数量的参数(过滤条件)。

使用内在转换(因此,您的代码知道如何处理参数),我会这样处理:

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)
  }
于 2013-10-22T14:50:42.660 回答
0

对于这种特殊情况,我会使用:

def f(dates: List[Date], oStart: Option[Long], oEnd: Option[Long]): List[Date] = {
  val start = oStart.getOrElse(Long.MinValue)
  val end = oEnd.getOrElse(Long.MaxValue)
  dates.filter(date => date.getTime > start && date.getTime < end)
}

您几乎可以肯定地为您的开始和结束参数概括为一些 Min/Max Monoid,并将其与 Ordering 结合以获得单线,但我将把它作为练习留给读者。

于 2013-10-22T22:54:25.410 回答
0

我同意同事的观点,最好在每种情况下都有单独的过滤器。为此,最好使用该.filter方法。但是,您还需要转换DataLong. 这可以通过隐式转换器来完成:

implicit def dateToLong(d:Date) = d.getTime

然后你只需进行过滤:

dates.view.filter(_ >= start).filter(_ <= finish)

如果您仍然需要可选过滤器,那么隐含的“pimp-my-library”可以提供帮助

implicit class ListWithOptionalFilters[T](datas:List[T]){
  def filterOpt(f:Option[T=>Boolean]) = 
    datas.filter(d => f.forall(d))

}

然后你这样做:

def f(dates: List[Date], start: Option[Long], end: Option[Long]) = {
  val startFilter = start.map(l => (d:Date) => d>=l)
  val endFilter = end.map(l => (d:Date) => d<=l)
  dates.filterOpt(startFilter).filterOpt(endFilter)
}
于 2013-10-22T14:40:39.680 回答
0
val l1 = startDate.map { d => dates.filter(_ > d) }
val l2 = endDate.map { d => dates.filter(_ < d) }
l1.getOrElse(dates).intersect(l2.getOrElse(dates))
于 2013-10-22T14:49:38.423 回答