Java 什么';s Scala';用分隔符拆分列表的惯用方法是什么?
如果我有一个字符串类型的列表Java 什么';s Scala';用分隔符拆分列表的惯用方法是什么?,java,list,scala,Java,List,Scala,如果我有一个字符串类型的列表 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, Fis
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”
,在本例中)
我曾经考虑过用indexOf
找到“BREAK”
的位置,然后用这种方法拆分列表,或者用类似的方法使用takeWhile(I=>I!=“BREAK”)
,但我想知道是否有更好的方法
如果有帮助的话,我知道在
项目
列表中只有3组项目(因此有2个“BREAK”
标记)。这样如何:使用扫描
来确定列表中的每个元素属于哪个部分
val l = List("Apple","Banana","Orange","Tomato","Grapes","BREAK","Salt","Pepper","BREAK","Fish","Chicken","Beef")
val count = l.scanLeft(0) { (n, s) => if (s=="BREAK") n+1 else n } drop(1)
val paired = l zip count
(0 to count.last) map { sec =>
paired flatMap { case (x, c) => if (c==sec && x!="BREAK") Some(x) else None }
}
// Vector(List(Apple, Banana, Orange, Tomato, Grapes), List(Salt, Pepper), List(Fish, Chicken, Beef))
def splitBySeparator[T](l:List[T],sep:T):List[List[T]={
l、 span(!=sep)匹配{
案例(hd,::tl)=>hd::拆分器(tl,sep)
案例(hd,)=>列表(hd)
}
}
结果:
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=>
如果(e==sep){
如果(!b.last.isEmpty)b+=ListBuffer[T]()
}
否则b.last+=e
}
b、 映射(toSeq)
}
它在内部使用了一个ListBuffer
,这与我在第一个版本的splitBySeparator
中使用的List.span
的实现非常相似。另一个选项是:
val l=Seq(1,2,3,4,5,9,1,2,3,4,5,9,1,2,3,4,5,9,1,2,2,3,4,5,9,1,2,3,4,5)
l、 foldLeft(Seq(Seq.empty[Int])){
(acc,i)=>
如果(i==9)acc:+Seq.empty
else acc.init:+(acc.last:+i)
}
//产生:
列表(列表(1,2,3,4,5)、列表(1,2,3,4,5)、列表(1,2,3,4,5)、列表(1,2,3,4,5))
这也不是尾部递归,但在边缘情况下也可以:
def splitsies[T](l:List[T], sep:T) : List[List[T]] = l match {
case head :: tail =>
if (head != sep)
splitsies(tail,sep) match {
case h :: t => (head :: h) :: t
case Nil => List(List(head))
}
else
List() :: splitsies(tail, sep)
case Nil => List()
}
唯一令人讨厌的是:
scala> splitsies(List("BREAK","Tiger"),"BREAK")
res6: List[List[String]] = List(List(), List(Tiger))
如果您想更好地处理分隔符启动的情况,请查看与Martin中使用span类似的内容(针对一个稍微不同的问题)
此处“,”是唯一的分隔符,不会出现在项目列表中的任何字符串中。如果需要,我们可以选择其他分隔符
items.mkString(“,”)
将所有内容组合成一个字符串
.split("BREAK") // which we then split using "BREAK" as delimiter to get a list
.map("(^,|,$)".r.replaceAllIn(_, "")) // removes the leading/trailing commas of each element of the list in previous step
.map(_.split(",")) // splits each element using comma as seperator to give a list of lists
scala> val q = items.mkString(",").split("BREAK").map("(^,|,$)".r.replaceAllIn(_, "")).map(_.split(","))
q: Array[Array[String]] = Array(Array(Apple, Banana, Orange, Tomato, Grapes), Array(Salt, Pepper), Array(Fish, Chicken, Beef))
scala> q(0)
res21: Array[String] = Array(Apple, Banana, Orange, Tomato, Grapes)
scala> q(1)
res22: Array[String] = Array(Salt, Pepper)
scala> q(2)
res23: Array[String] = Array(Fish, Chicken, Beef)
使用List.unfold(Scala 2.13及更高版本):
代码在上运行
使用reverse
+foldLeft
:
def splitatement[T](列表:列表[T],元素:T):列表[list[T]={
list.reverse.foldLeft(list(list[T]())((l,currentElement)=>{
if(currentElement==元素){
List()::l
}否则{
(currentElement::l.head)::l.tail
}
})
}
代码在上运行
使用foldRight
:
def splitBySeparator[T](列表:列表[T],九月:T):列表[list[T]={
foldRight(list(list[T]())((s,l)=>{
如果(sep==s){
List()::l
}否则{
(s::l.头)::l.尾
}
}).filter(u.nonEmpty)
}
代码运行于。
splitAt
可能更合适,但恼人的是,BREAK
仍然存在,您需要手动删除它。这个问题类似,但不完全相同:+1对Luigi来说,这是Martin Odersky自己的答案:)这不是尾部递归,所以在长列表中要小心使用,很好的一点——我的名单会比较短,所以我不必担心。@stew:的确如此。我用一个尾部递归的版本更新了答案(同时有大量的边缘案例)。我确实遇到了列表(“BREAK”)
边缘案例,因此感谢您的修订,在列表的末尾添加元素是不有效的
val q = items.mkString(",").split("BREAK").map("(^,|,$)".r.replaceAllIn(_, "")).map(_.split(","))
.split("BREAK") // which we then split using "BREAK" as delimiter to get a list
.map("(^,|,$)".r.replaceAllIn(_, "")) // removes the leading/trailing commas of each element of the list in previous step
.map(_.split(",")) // splits each element using comma as seperator to give a list of lists
scala> val q = items.mkString(",").split("BREAK").map("(^,|,$)".r.replaceAllIn(_, "")).map(_.split(","))
q: Array[Array[String]] = Array(Array(Apple, Banana, Orange, Tomato, Grapes), Array(Salt, Pepper), Array(Fish, Chicken, Beef))
scala> q(0)
res21: Array[String] = Array(Apple, Banana, Orange, Tomato, Grapes)
scala> q(1)
res22: Array[String] = Array(Salt, Pepper)
scala> q(2)
res23: Array[String] = Array(Fish, Chicken, Beef)
val p: String => Boolean = _ != "BREAK"
val result: List[List[String]] = List.unfold(items) {
case Nil =>
None
case l if p(l.head) =>
Some(l.span(p))
case _ :: tail =>
Some(tail.span(p))
}