blue-sky
blue-sky

Reputation: 53806

Convert String list into Map[String, List]

I'm trying to convert List("a,1" , "b,2" , "c,3" , "a,2" , "b,4") to type scala.collection.immutable.HashMap[String, java.util.List[String]] with values :

a -> 1,2
b -> 2,4
c -> 3

So each key contains a List of its values.

Here is my code so far :

object ConvertList extends Application {

  var details = new scala.collection.immutable.HashMap[String, java.util.List[String]]

  val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")

  //Get all values
  val getValue : Function1[String, String] = { a => a.split(",")(1) }
  val allValues : List[String] = strList map getValue

  //get unique values
  val uniqueValues = allValues.toSet[String]

  //Somehow map each unique value to a value in the original List....
  println(uniqueValues)

  println(strList.flatten)
  //userDetails += "1" -> List("a","b",


}

How can this conversion be performed ?

Upvotes: 10

Views: 18046

Answers (5)

Xavier Guihot
Xavier Guihot

Reputation: 61666

Starting Scala 2.13, we can use the new groupMap method which (as its name suggests) is a one-pass equivalent of a groupBy and a mapping over grouped items:

// val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")
strList.map(_.split(",")).groupMap(_(0))(_(1))
// Map("b" -> List(2, 4), "a" -> List(1, 2), "c" -> List(3))

This:

  • splits each string (producing List(Array(a, 1), Array(b, 2), ...))

  • groups elements based on their first part (_(0)) (group part of groupMap)

  • maps grouped elements to their second part (_(1)) (map part of groupMap)

Upvotes: 0

Jens Egholm
Jens Egholm

Reputation: 2710

There are already a good deal of takes, but what about something similar to what Marth proposes:

import scala.collection.JavaConverters._

val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")

strList.map(_.split(',')).collect { 
  case Array(key, value) => key -> value 
}.groupBy(_._1).mapValues(_.map(_._2).asJava)

This relies heavily on functional programming and ends up with a Map of type Map[String, java.util.List[String]], while not just taking fixed positions in the input string, but splitting at the comma (imagine having numbers over 9, requiring more than one digit).

Also, if there are more than one value from the split, the collect method filters them away.

Upvotes: 3

Brian
Brian

Reputation: 20285

scala> List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")
res0: List[String] = List(a,1, b,2, c,3, a,2, b,4)

scala> res0.groupBy(xs => xs.split(",")(0)).mapValues(xs => xs.flatMap(xs => xs.toCharArray.filter(_.isDigit)))
res2: scala.collection.immutable.Map[String,List[Char]] = Map(b -> List(2, 4), a -> List(1, 2), c -> List(3))

Using groupBy makes this straight forward since you want a Map. The groupBy splits each element of the List by , and takes the first one which is the key. That gives this: scala.collection.immutable.Map[String,List[String]] = Map(b -> List(b,2, b,4), a -> List(a,1, a,2), c -> List(c,3)). From here it is just processing to get the digits from each List of values.

This returns a Map[String, List[Char]]. There is a little more to do if you want scala.collection.immutable.HashMap[String, java.util.List[String]] returned but that's the easy part.

Upvotes: 1

Marth
Marth

Reputation: 24812

strList.map(s => (s(0).toString,s(2).toString))
       .groupBy(_._1)
       .mapValues(_.map(_._2))

Output :

Map[String,List[String]] = Map(b -> List(2, 4), a -> List(1, 2), c -> List(3))

Upvotes: 15

om-nom-nom
om-nom-nom

Reputation: 62835

Lists wouldn't be in the same order, but generally it is quite feasible problem:

// for a sake of pithiness
type M = Map[String,List[String]] 
def empty: M = Map.empty.withDefaultValue(Nil)

@annotation.tailrec
def group(xs: List[String], m: M = empty): M = xs match {
    case Nil     => m
    case h::tail => 
      val Array(k,v) = h.split(",")
      val updated = v::m(k)
      combine(tail, m + (k -> updated))
}

Upvotes: 3

Related Questions