List 根据scala中的元素类型将列表拆分为子列表

List 根据scala中的元素类型将列表拆分为子列表,list,scala,List,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) ) 有些冗长,部分原因

如何根据元素类型将列表拆分为子列表

简言之,鉴于:

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

有些冗长,部分原因是它们是case对象。

为什么不在列表中只使用
groupBy

val rows = drinks.groupBy(x => x).values.toList

可以测试

您可以使用短尾递归函数,如下所示

其思想是使用
next
将“要附加到结果的下一个饮料列表”和
acc
将这些饮料列表累积到一个饮料列表中

基本情况是一个空列表,其中返回结果。否则,要么下一个饮料匹配下一个子列表(将其添加到此子列表),要么不匹配(将子列表添加到结果中,并使用新饮料启动新的子列表)

请注意,
:+
是一个列表方法,它返回一个新列表,并附加指定的项

@tailrec
def获取(列表:列表[饮料],
下一步:List[Drink]=List(),
acc:List[List[Drink]=List()):List[List[Drink]]=
列表匹配{
案例Nil=>acc:+next//dump最终结果
案例头部::尾部=>
if(next.isEmpty | | next.head.getClass==head.getClass)get(tail,next:+head,acc)
else get(尾部、列表(头部)、acc:+next)
}
println(获取(饮料))
结果:

List(List(Coke, Coke), List(Pepsi), List(Coke), List(Pepsi, Pepsi))
注意,注意jwvh也有一个正确的答案,使用正确的模式匹配而不是这些条件。在列表中使用
head
方法可能不安全(或者编译器很难确定安全性),但这种方法可能更简洁,尤其是在有多种饮料的情况下

如果您想避免直接使用
head
,可以这样写,我发现这更让人困惑:

。。。
if(next.headOption.map(h=>h.getClass==head.getClass).getOrElse(true))get(tail,next:+head,acc)
...
如果类型是参数化的,则需要注释中提到的
TypeTags
。。。但在这一点上,只需在类本身中添加一个方法就很容易了。。。比如:

    class Drink[T: ClassTag] {
       def typeId = s"Drink of ${classTag[T].runtimeClass.getName}"
    }

然后,您就可以比较这些类型ID而不是实际的类。

您想要一些泛型的东西,还是对一组特定类型的解决方案满意?此外,它们是否始终是case对象,或者也可能是case类最后,你试过什么?@LuisMiguelMejíaSuárez这套特殊的类型是不相关的。现实世界将是案例类。这些案例类将是参数化/泛型的吗?类似于
最终案例类Foo[T](数据:T)扩展了Bar
?如果是这样的话,你有
列表(Foo(1),Foo(“3”))
这两个是在同一组还是在不同的组中?是的,当然,我只是没有将它们参数化以简化问题。那么,由于类型擦除,你的问题基本上无法解决。-你有四个选择:1。简化数据类型,以避免类型擦除-2。只依赖于类测试,这意味着
Foo(1)
Foo(“3”)
将属于同一组-3。使用TypeTags,但这需要创建列表的人用其TypeTag对每个元素进行压缩。-4.与上一个类似,创建自己的
类型
ADT,并在创建列表时使用其
类型
压缩每个元素。这不是OP要求的,它甚至只适用于
案例对象
    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
    }
    class Drink[T: ClassTag] {
       def typeId = s"Drink of ${classTag[T].runtimeClass.getName}"
    }