Scala 按字符串int*模式对列表元素进行分组

Scala 按字符串int*模式对列表元素进行分组,scala,scala-collections,Scala,Scala Collections,我有一份清单: List[Any]("foo", 1, 2, "bar", 3, 4, 5) 我想以这种方式对其元素进行分组: Map("foo" -> List(1,2), "bar" -> List(3,4,5)) 我只能想象一个带有可变列表和变量的命令式解决方案,但解决此任务的正确方法是什么 def toMap(seq: Seq[Any]) = { val (result, _) = seq.foldRight((Map.empty[String, Seq[Int]

我有一份清单:

List[Any]("foo", 1, 2, "bar", 3, 4, 5)
我想以这种方式对其元素进行分组:

Map("foo" -> List(1,2), "bar" -> List(3,4,5))
我只能想象一个带有可变列表和变量的命令式解决方案,但解决此任务的正确方法是什么

def toMap(seq: Seq[Any]) = {
    val (result, _) = seq.foldRight((Map.empty[String, Seq[Int]], "")) {
      (el, acc) =>
        val (map, str) = acc
        el match {
          case s: String =>
            (map, s)
          case number: Int =>
            val el = map.getOrElse(str, Seq[Int]())
            (map + (str -> (number +: el)), str)
        }
    }
    result
  }
==测试==

scala> val list = List[Any]("foo", 1, 2, "bar", 3, 4, 5)
list: List[Any] = List(foo, 1, 2, bar, 3, 4, 5)

scala> toMap(list)
res3: scala.collection.immutable.Map[String,Seq[Int]] = Map(foo -> List(2, 1), bar -> List(5, 4, 3))

考虑中定义的
multiSpan
;那么

val a = List[Any]("foo", 1, 2, "bar", 3, 4, 5)
我们有

val b = a.multiSpan(_.isInstanceOf[String])
b: List[List[Any]] = List(List(foo, 1, 2), List(bar, 3, 4, 5))
诸如此类

b.map { l => (l.head, l.tail) }.toMap
res: Map(foo -> List(1, 2), bar -> List(3, 4, 5))
递归方式:

@tailrec
def toMap(list:List[Any], acc:Map[String,List[Int]] = Map()):Map[String,List[Int]] = list match {
  case Nil => acc
  case List(key:String, _*) => {
    val values = list.drop(1).takeWhile(_.isInstanceOf[Int]).map {case i:Int => i}
    toMap(list.drop(1+values.size), acc + (key -> values))
  }
}
似乎工作正常:

scala> toMap(list)
res0: Map[String,List[Any]] = Map(foo -> List(1, 2), bar -> List(3, 4, 5))
直截了当

如果列表中有元素,则从
尾部
获取所有整数,并删除剩余的整数,该整数存储在
rest
中。这就是
span
所做的。然后将
头部
映射到
内部
并递归地为
其余部分
执行此操作

def f(a: List[Any]): Map[String, List[Int]] = a match {
  case Nil => Map.empty
  case head :: tail => {
    val (ints, rest) = tail.span(_.isInstanceOf[Int])
    f(rest) + (head.asInstanceOf[String] -> ints.map(_.asInstanceOf[Int]))
  }
}

我们的解决方案非常相似。不过,我建议您使用
span
,这样您可以同时上下。