10

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 ?

2
  • are you strict on immutable hashmap? Commented Aug 29, 2013 at 20:40
  • @om-nom-nom no, im just trying to avoid an imperative solution. Commented Aug 29, 2013 at 20:43

5 Answers 5

15
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))
Sign up to request clarification or add additional context in comments.

3 Comments

Could you explain 's(0).toString,s(2).toString)' ? Is it mapping each string element to a String,String tuple ?
Nice answer. Indexing on the string really makes this solution clean. Have an upvote.
Yes, it is mapping every string to the couple ('first letter', 'third letter'). (so "a,1" to ("a","1")). You could (and should it this code is more than a brain teaser) code a function formatString(s) and use strList.map(formatString).groupBy(...) to better handle special case (string.length < 3, etc)
3

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

Comments

3

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.

Comments

1
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.

Comments

0

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.