Scala如何将List的等值元素转换为子列表

时间:2014-11-20 00:28:45

标签: scala

case class E(timestamp: Long, value: Double)

我有一个实例E列表。列表中的元素按时间戳排序。我想将具有0值的元素块分组到子列表中并删除非零值。例如,

val xs = List(E1, E2, E3, E4, E5, E6, E7, E8, E9, E10)

其中E2, E3, E4, E7, E8, E10的值为0

result = List(List(E2, E3, E4), List(E7, E8), List(E10))

在Scala中执行此操作的最佳方法是什么?谢谢!

3 个答案:

答案 0 :(得分:2)

这是一行(使用foldLeft +过滤器):

scala> val l = List(1, 0, 0, 0, 2, 3, 0, 0, 5, 6, 0)
l: List[Int] = List(1, 0, 0, 0, 2, 3, 0, 0, 5, 6, 0)

scala> (l ++ List(1)).foldLeft((List(List[Int]()), List[Int]()))((a, b) => if (b != 0) (a._1 +: a._2, Nil) else (a._1, a._2 +: b))._1.filter(_.nonEmpty)
res26: List[List[Int]] = List(List(0, 0, 0), List(0, 0), List(0))

更易阅读的版本:

def groupByPred[T](l: List[T], predicate: T => Boolean = (x: T) => x == 0) = {
    case class Acc(perm: List[List[T]] = Nil, temp: List[T] = Nil)
    val raw =  l.foldLeft(Acc())((a, b) => 
        if (!predicate(b)) Acc(a.perm :+ a.temp, Nil) else Acc(a.perm,  a.temp :+ b)) 
    (raw.perm :+ raw.temp).filter(_.nonEmpty)
}

请注意":+"连接需要O(n),所以最好在这里使用ListBuffer而不是List。

答案 1 :(得分:1)

这是一个通用的方法,可以为您提供所需的内容:

  def splitBy[A](l: List[A])(pred: A => Boolean): List[List[A]] = {
    l match {
      case Nil => Nil
      case _ =>
        val res = l.dropWhile(a => !pred(a))
        val (acc, r) = res.span(pred)
        acc :: splitBy(r)(pred)
    }
  }

以下是我设置的一般示例:

  val E1 = E(1, 1)
  val E2 = E(2, 0)
  val E3 = E(3, 0)
  val E4 = E(4, 0)
  val E5 = E(5, 1)
  val E6 = E(6, 1)
  val E7 = E(7, 0)
  val E8 = E(8, 0)
  val E9 = E(9, 1)
  val E10 = E(10, 0)

  val xs = List(E1, E2, E3, E4, E5, E6, E7, E8, E9, E10)

  println(splitBy(xs)(_.value == 0.0))
  //prints List(List(E(2,0.0), E(3,0.0), E(4,0.0)), List(E(7,0.0), E(8,0.0)), List(E(10,0.0)))

请注意,这基本上是列表中的foldRight并且不是堆栈友好的,例如这会导致堆栈溢出println(splitBy(List.fill(10000)(xs).flatten)(_.value == 0.0))

这是一个堆栈安全版本:

  @tailrec
  def splitBy[A](l: List[A], accum:List[List[A]] = Nil)(pred: A => Boolean): List[List[A]] = {
    l match {
      case Nil => accum.reverse
      case _ =>
        val res = l.dropWhile(a => !pred(a))
        val (acc, r) = res.span(pred)
        splitBy(r, acc :: accum)(pred)
    }
  }

答案 2 :(得分:0)

以下是使用takeWhiledropWhile的相当惯用的方法。

def subLists[A](xs: List[A], p: A => Boolean):List[List[A]] = xs match{
  case List() => List()
  case h::t => { 
    if(p(h)){ 
      xs.takeWhile(p) :: subLists(xs.dropWhile(p), p)
    }else{
      subLists(t, p)
    }
  }
}

输出

scala> subLists(xs, isEZero)      │
res6: List[List[E]] = List(List(E(3,0.0), E(4,0.0), E(5,0.0)), List(E(7,0.0), E(8,0.0)), List(E(10,0.0))) 

要为后续人员复制/粘贴的代码/数据。

def isEZero(e:E) = e.value == 0.0

val E1 = E(1L, 1.0)
val E2 = E(2L, 1.0)
val E3 = E(3L, 0.0)
val E4 = E(4L, 0.0)
val E5 = E(5L, 0.0)
val E6 = E(6L, 1.0)
val E7 = E(7L, 0.0)
val E8 = E(8L, 0.0)
val E9 = E(9L, 1.0)
val E10 = E(10L, 0.0)

val xs = List(E1,E2,E3,E4,E5,E6,E7,E8,E9,E10)