Scala按分隔符拆分列表的惯用方法是什么?

2022-09-04 20:41:52

如果我有一个字符串类型的列表,

scala> val items = List("Apple","Banana","Orange","Tomato","Grapes","BREAK","Salt","Pepper","BREAK","Fish","Chicken","Beef")
items: List[java.lang.String] = List(Apple, Banana, Orange, Tomato, Grapes, BREAK, Salt, Pepper, BREAK, Fish, Chicken, Beef)

我如何根据某个字符串/模式将其拆分为单独的列表(在这种情况下)。n"BREAK"

我曾想过找到 with 的位置,并以这种方式拆分列表,或者使用类似的方法,但我想知道是否有更好的方法?"BREAK"indexOftakeWhile (i => i != "BREAK")

如果它有帮助,我知道列表中只有3组项目(因此2个标记)。items"BREAK"


答案 1
def splitBySeparator[T](l: List[T], sep: T): List[List[T]] = {
  l.span( _ != sep ) match {
    case (hd, _ :: tl) => hd :: splitBySeparator(tl, sep)
    case (hd, _) => List(hd)
  }
}
val items = List("Apple","Banana","Orange","Tomato","Grapes","BREAK","Salt","Pepper","BREAK","Fish","Chicken","Beef")
splitBySeparator(items, "BREAK")

结果:

res1: List[List[String]] = List(List(Apple, Banana, Orange, Tomato, Grapes), List(Salt, Pepper), List(Fish, Chicken, Beef))

更新:上面的版本虽然简洁有效,但有两个问题:它不能很好地处理边缘情况(如或),并且不是尾递归。所以这是另一个(命令式)版本来解决这个问题:List("BREAK")List("BREAK", "Apple", "BREAK")

import collection.mutable.ListBuffer
def splitBySeparator[T](l: Seq[T], sep: T): Seq[Seq[T]] = {
  val b = ListBuffer(ListBuffer[T]())
  l foreach { e =>
    if ( e == sep ) {
      if  ( !b.last.isEmpty ) b += ListBuffer[T]()
    }
    else b.last += e
  }
  b.map(_.toSeq)
}

它在内部使用 一个 ,就像我在第一个版本中使用的实现一样。ListBufferList.spansplitBySeparator


答案 2

另一种选择:

val l = Seq(1, 2, 3, 4, 5, 9, 1, 2, 3, 4, 5, 9, 1, 2, 3, 4, 5, 9, 1, 2, 3, 4, 5)

l.foldLeft(Seq(Seq.empty[Int])) {
  (acc, i) =>
    if (i == 9) acc :+ Seq.empty
    else acc.init :+ (acc.last :+ i)
}

// produces:
List(List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5))