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

时间:2014-05-28 11:44:54

标签: scala scala-collections

我有一个清单:

List[Any]("foo", 1, 2, "bar", 3, 4, 5)

我想用这种方式对它进行分组:

Map("foo" -> List(1,2), "bar" -> List(3,4,5))

我只能想象一个具有可变列表和变量的命令式风格的解决方案,但是解决此任务的正确方法是什么?

4 个答案:

答案 0 :(得分:1)

考虑https://stackoverflow.com/a/21803339/3189923中定义的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))

答案 1 :(得分:1)

递归方式:

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

答案 2 :(得分:1)

简单。

如果列表中包含元素,请从tail中取出所有整数,然后删除其余的整数,这些内容存储在rest中。这就是span的作用。然后将head映射到ints,然后针对rest进行递归。

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

答案 3 :(得分:0)

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