3

I have a list:

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

I would like to group it's elements this way:

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

I can only imagine a solution in an imperative style with mutable lists and vars, but what is the proper way of solving this task?

shutty
  • 3,298
  • 16
  • 27

4 Answers4

1

Consider multiSpan as defined in https://stackoverflow.com/a/21803339/3189923 ; then for

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

we have that

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

and so

b.map { l => (l.head, l.tail) }.toMap
res: Map(foo -> List(1, 2), bar -> List(3, 4, 5))
Community
  • 1
  • 1
elm
  • 20,117
  • 14
  • 67
  • 113
1

Recursive way:

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

Seems to work correctly:

scala> toMap(list)
res0: Map[String,List[Any]] = Map(foo -> List(1, 2), bar -> List(3, 4, 5))
shutty
  • 3,298
  • 16
  • 27
  • Our solutions are pretty similar. I would advise you to use `span` though, so you can take and drop at the same time. – Kigyo May 28 '14 at 13:30
1

Straightforward.

If the list has elements, take all ints from the tail and drop the rest, which is stored in rest. That is what span does. Then map the head to the ints and recursivly do it for the 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]))
  }
}
Kigyo
  • 5,668
  • 1
  • 20
  • 24
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
  }

===Test==

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))
Andrzej Jozwik
  • 14,331
  • 3
  • 59
  • 68