3

groupBy is defined as : def groupBy[K](f: A => K): immutable.Map[K, Repr] = {

So f is a function which takes an A and returns K , K is the current type so in below example is List ?

Using below :

  val l : List[(String , String)] = List( ("a" , "line1") , ("b" , "line2") , ("b" , "line3") , ("a" , "line4"))
  val gm : Map[String,List[(String, String)]] = l.groupBy(_._1)

I'm attempting to convert to type :

  val m : Map[String , List[String]] = Map("a" -> List("line1" , "line4") , "b" -> List("line2" , "line3"))

But instead I receive type : Map[String,List[(String, String)]]

How to amend groupBy(_._1) to return excepted type ? Why is _._1 a function of type A => K ?

blue-sky
  • 51,962
  • 152
  • 427
  • 752

3 Answers3

3

It might be a bit easier to follow if we will introduce type aliases:

scala> type Key = String
defined type alias Key

scala> type Value = String
defined type alias Value

scala> val l: List[(Key, Value)] = List("a" -> "line1", "b" -> "line2", "b" -> "line3", "a" -> "line4")
// l: List[(Key, Value)] = List((a,line1), (b,line2), (b,line3), (a,line4))

scala> l.groupBy(_._1)
// res0: scala.collection.immutable.Map[Key,List[(Key, Value)]] = Map(b -> List((b,line2), (b,line3)), a -> List((a,line1), (a,line4)))

Now it's evident that groupBy takes Key out of (Key, Value) pairs, but lefts pairs intact, just aggregates them together basing on Key. What we need to do is to select values out of this grouped pairs:

scala> res0.mapValues(group => group.map(kv => kv._2))
// res2: scala.collection.immutable.Map[Key,List[Value]] = Map(b -> List(line2, line3), a -> List(line1, line4))
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • With the usual warning that unlike `map`, `mapValues` only wraps the input map and doesn't produce a new independent one which can result in strange behaviour. – 0__ Aug 13 '15 at 09:59
  • 1
    @0__ is this holds for immutable.Map? Can you, please, elaborate on this? It's the first time I am hearing about this issue – om-nom-nom Aug 13 '15 at 10:10
  • Out of my head: 1. careful with side-effects in the function (yes, you shouldn't do that, but my experience says you occasionally do); 2. map is not serializable. I have simply decided not to use that method ever again, and my life got better. – 0__ Aug 13 '15 at 10:49
1

groupBy takes an input collection of type Repr (in your case List[(String, String)] and applies a key-generating function f to it, storing the former elements of Repr as values in a Map[K, Repr].

So with that method there is no way to transform the values at the same time. You want to extract a key and value. You want to end up with a sort of "multi-map".

Why is _._1 a function of type A => K ?

Your function receives elements of the input collection, which is a Tuple2 of strings, so _._1 is short for ((a: String, b: String)) => a.

The quick solution is to map the result one more time instead:

l.groupBy(_._1).map { case (key, list) => (key, list.map(_._2)) }
0__
  • 66,707
  • 21
  • 171
  • 266
1

You have a List[(String, String)] then you do a groupBy on the first value of that tuple and ends up with a Map[String, List[(String, String)]]. So you have the correct key, but the wrong value. You need to shed the first value in the lists tuples.

That can be done with mapValues to change the lists (in plural) from List[(String, String)] to List[String]:

val l : List[(String , String)] = List( ("a" , "line1") , ("b" , "line2") , ("b" , "line3") , ("a" , "line4"))
val gm : Map[String,List[(String, String)]] = l.groupBy(_._1)
val m : Map[String, List[String]] = gm.mapValues(_.map(_._2))

The A is the type of the list and the K is the type of the new group key you are creating. The function A => K allows you to create any type of key type K from the list type A.

Notice that the A type of the lists is maintained in the lists within the map.

thoredge
  • 12,237
  • 1
  • 40
  • 55