1

I have a List(List("aba, 4"), List("baa, 2"))and I want to convert it into a map:

val map : Map[String, Int] = Map("aba" -> 4, "baa" -> 2)

What's the best way to archive this?

UPDATE:

I do a database query to retrieve the data: val (_, myData) = DB.runQuery(...)

This returns a Pair but I'm only interested in the second part, which gives me:

myData: List[List[String]] = List(List(Hello, 19), List(World, 14), List(Foo, 13), List(Bar, 13), List(Bar, 12), List(Baz, 12), List(Baz, 11), ...)
user1243091
  • 175
  • 3
  • 11

4 Answers4

8
scala> val pat = """\((.*),\s*(.*)\)""".r
pat: scala.util.matching.Regex = \((.*),\s*(.*)\)

scala> list.flatten.map{case pat(k, v) => k -> v.toInt }.toMap
res1: scala.collection.immutable.Map[String,Int] = Map(aba -> 4, baa -> 2)
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • +1, folks, this is a very, very good answer. One thing though, needs, "k -> v.toInt" to match Map[String, Int] as specified in the question – virtualeyes Apr 08 '12 at 14:54
  • @virtualeyes, oh yeah I didn't see about the `Int` type. Fixed as suggested. – huynhjl Apr 08 '12 at 15:32
4

Yet another take:

List(List("aba, 4"), List("baa, 2")).
  flatten.par.collect(
    _.split(",").toList match {
      case k :: v :: Nil => (k, v.trim.toInt) 
  }).toMap

Differences to the other answers:

  • uses .par to parallelize the creation of the pairs, which allows us to profit from multiple cores.
  • uses collect with a PartialFunction to ignore strings that are not of the form "key, value"

Edit: .par does not destroy the order as the answer state previously. There is only no guarantee for the execution order of the list processing, so the functions should be side-effect free (or side-effects shouldn't care about the ordering).

Community
  • 1
  • 1
Frank
  • 10,461
  • 2
  • 31
  • 46
  • One little note: `.par` is available only in 2.9+ versions of Scala. (and are you sure, that `.par` destroys the order?) – om-nom-nom Apr 08 '12 at 13:21
  • Thanks for spotting this. I messed up that the intermediate processing is non-ordered (f.ex. `println` calls), but the resulting values will be pieced together in the same order again. – Frank Apr 08 '12 at 15:58
1

My take:

List(List("aba, 4"), List("baa, 2")) map {_.head} map {itemList => itemList split ",\\s*"} map {itemArr => (itemArr(0), itemArr(1).toInt)} toMap

In steps:

List(List("aba, 4"), List("baa, 2")).
  map(_.head).                                    //List("aba, 4", "baa, 2")
  map(itemList => itemList split ",\\s*").        //List(Array("aba", "4"), Array("baa", "2"))
  map(itemArr => (itemArr(0), itemArr(1).toInt)). //List(("aba", 4), ("baa", 2))
  toMap                                           //Map("aba" -> 4, "baa" -> 2)

Your input data structure is a bit awkward so I don't think you can optimize it/shorten it any further.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • It works with the small provided list. However, when I try this with the data list I have I get a java.lang.ArrayIndexOutOfBoundsException: 1... Can I guard against it somehow? – user1243091 Apr 08 '12 at 11:53
  • @user1243091: The size of the list does not matter here. I guess the problem is `(itemArr(0), itemArr(1).toInt)` which means some of your input string do not follow `"string, int"` pattern. Try running this on your input: `List(List("aba, 4"), List("baa, 2")) map {_.head} map {itemList => itemList split ",\\s*"} filterNot {_.size == 2}` - it should **not** return anything – Tomasz Nurkiewicz Apr 08 '12 at 11:58
  • It returns something, actually everything in my List[List[String]]. That is interesting... I don't understand. The entries in my List[String] are really looking like the provided sample data... – user1243091 Apr 08 '12 at 12:38
1
List(List("aba, 4"), List("baa, 2")).
  flatten.     //get rid of those weird inner Lists
  map {s=> 
    //split into key and value
    //Array extractor guarantees we get exactly 2 matches
    val Array(k,v) = s.split(","); 
    //make a tuple out of the splits
    (k, v.trim.toInt)}.
  toMap  // turns an collection of tuples into a map
Adam Rabung
  • 5,232
  • 2
  • 27
  • 42