![](/img/trans.png)
[英]Scala select an element from a list based on that element's sublist properties
[英]Split a list into sublist based on element types in scala
如何根據元素類型將列表拆分為子列表?
簡而言之,鑒於:
trait Drink
final case object Coke extends Drink
final case object Pepsi extends Drink
val drinks = List(Coke,Coke,Pepsi,Coke,Pepsi,Pepsi)
我想:
List( List(Coke,Coke), List(Pepsi), List(Coke), List(Pepsi, Pepsi) )
drinks.foldRight[List[List[Drink]](List.empty) {
case (next, (l@(last :: _) :: tail) if next.getClass == last.getClass =>
(next :: l)::tail
case (next, rest) => List(next) :: rest
}
如果類型是參數化的,您將需要注釋中提到的TypeTags
......但在這一點上,向 class 本身添加一個方法真的會更容易......類似於:
class Drink[T: ClassTag] {
def typeId = s"Drink of ${classTag[T].runtimeClass.getName}"
}
然后你可以只比較這些類型 id 而不是實際的類。
drinks.foldRight(List.empty[List[Drink]]){
case (c:Coke.type, ((hd:Coke.type)::tl)::acc) => (c::hd::tl)::acc
case (p:Pepsi.type,((hd:Pepsi.type)::tl)::acc) => (p::hd::tl)::acc
case (d, acc) => List(d)::acc
}
有點冗長,部分原因是它們是案例對象。
您可以使用短尾遞歸 function,如下所示。
想法是使用next
將“下一個飲品列表到 append 到結果”並acc
將這些飲品列表累積到飲品列表中。
基本情況是一個空列表,其中返回結果。 否則,下一個飲料與下一個子列表匹配(將其添加到此子列表),或者不匹配(將子列表添加到結果並使用新飲料開始一個新的子列表)。
請注意:+
是一個 List 方法,它返回一個附加了指定項目的新 List。
@tailrec
def get(list:List[Drink],
next:List[Drink]=List(),
acc: List[List[Drink]]=List()): List[List[Drink]] =
list match {
case Nil => acc :+ next // dump final results
case head :: tail =>
if (next.isEmpty || next.head.getClass == head.getClass) get(tail, next :+ head, acc)
else get(tail, List(head), acc :+ next)
}
println(get(drinks))
結果:
List(List(Coke, Coke), List(Pepsi), List(Coke), List(Pepsi, Pepsi))
注意,注意到 jwvh 也有一個正確的答案,使用正確的模式匹配而不是這些條件。 在 List 上使用head
方法可能是不安全的(或者編譯器很難確定安全性),但這種方法可能更簡潔,尤其是在存在多種 Drink 時。
如果你想避免直接使用head
,你可以這樣寫,我覺得這更令人困惑:
...
if (next.headOption.map(h => h.getClass == head.getClass).getOrElse(true)) get(tail, next :+ head, acc)
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.