How to group by keys in a list - scala

I have a list in Scala that I want to group by a key and sum up the values of each key:
val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
I tried this code:
l.groupBy(identity).mapValues(_.sum)
But got the following error:
error: type mismatch;
found : scala.collection.immutable.Map[(String, Int),Int]
required: Seq[(String, Int)]

It might have been already answered in stackoverflow, but you group and then sum the values of list.
scala> val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
.groupBy(_._1)
.map { case (k, v) => k -> v.map { _._2}.sum}
l: scala.collection.immutable.Map[String,Int] = HashMap(cbe -> 5, abc -> 3, cab -> 1)

Given List:
val list = List(("abc",1),("abc",2),("cbe",5),("cab",1))
Using,
list.groupBy(_._1).mapValues(_.map(_._2).sum)
In Scala REPL:
scala> list.groupBy(_._1).mapValues(_.map(_._2).sum)
res13: scala.collection.immutable.Map[String,Int] = Map(cab -> 1, abc -> 3, cbe -> 5)

Can also be done with groupMapReduce
scala> val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
.groupMapReduce(_._1)(_._2)(_ + _)
val l: scala.collection.immutable.Map[String,Int] = Map(abc -> 3, cab -> 1, cbe -> 5)

Related

In Scala, what does x=> x._1._1 denotes

In the following snippet of code, I am aware that x._1 denotes the first element of the tuple, but I couldn't understand what x._1._1 represents.I am not so familiar with Scala, sorry if it is a relatively naive question, thank you!!
val a = b.groupBy(x=> x._1._1)
Here is a quick example in the REPL of a nested tuple
scala> val t = ((1, 2), 3)
t: ((Int, Int), Int) = ((1,2),3)
scala> t._1 // Get the first part of the tuple
res0: (Int, Int) = (1,2)
scala> t._2 // Get the second part of the tuple
res1: Int = 3
scala> t._1._1 // Get the first part of the first part
res2: Int = 1
And here is an example with a sequence to demonstrate the groupBy:
scala> val s = Seq(((1, 2), 3), ((1, 5), 6), ((2, 4), 32))
s: Seq[((Int, Int), Int)] = List(((1,2),3), ((1,5),6), ((2,4),32))
scala> s.groupBy
def groupBy[K](f: (((Int, Int), Int)) => K): scala.collection.immutable.Map[K,Seq[((Int, Int), Int)]]
scala> s.groupBy(x => x._1._1)
res3: scala.collection.immutable.Map[Int,Seq[((Int, Int), Int)]] = Map(2 -> List(((2,4),32)), 1 -> List(((1,2),3), ((1,5),6)))
In this case the first element of the first element are the target for the grouping. Here's the result in an easier to look at format:
Map(
2 -> List(
((2,4),32)),
1 -> List(
((1,2),3),
((1,5),6))
)
It means x._1 itself is a tuple.
Example:
val b = Seq((("subTuple_1", "subTuple_2"), "tuple_2"))
val a = b.groupBy(x=> x._1._1)
As you mentioned ._1 gives you the first column of your tuple, and if the result of first column is Tuple, you can do ._1.
eg.
scala> Map(("a" -> "b") -> 100, ("c" -> "d") -> 200).map(_._1)
res31: scala.collection.immutable.Map[String,String] = Map(a -> b, c -> d)
scala> Map(("a" -> "b") -> 100, ("c" -> "d") -> 200).map(_._1._1)
res32: scala.collection.immutable.Iterable[String] = List(a, c)
groupBy,
scala> Map(("a" -> "b") -> 100, ("a" -> "c") -> 200).groupBy(_._1._1)
res19: scala.collection.immutable.Map[String,scala.collection.immutable.Map[(String, String),Int]] = Map(a -> Map((a,b) -> 100, (a,c) -> 200))

Optional tuples to Map in Scala

If I have a List of tuples, I can convert to a map with toMap:
val x = (3 -> 3)
List(x).toMap
and I get
scala.collection.immutable.Map[Int,Int] = Map(3 -> 3)
If I have a List of Optional and try the same I would get an error:
val x = Some(3 -> 3)
val y = None
List(x, y).toMap
<console>:15: error: Cannot prove that Some[(Int, Int)] <:< (T, U).
I want to get the same result. Is it possible?
You can use flatten on the List to remove the Nones:
val x = Some(3 -> 3)
val y = None
List(x, y).flatten.toMap
> scala.collection.immutable.Map[Int,Int] = Map(3 -> 3)

How to set value in scala Map?

I am new to scala. I have a Map. I want to set a value in the Map with a particular key. Here is the code I am writing -
var mp: Map[Int, ParticipationStateTransition] = Map.empty[Int, ParticipationStateTransition]
val change: ParticipationStateTransition = new ParticipationStateTransition
mp(ri.userID) = change
The error it is showing me on the third line is -
application does not take parameters
What am I doing wrong? Thanks in advance.
Use .updated :
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val n = m.updated(1, 3)
n: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3)
scala> m
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> n
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3)
Note that scala's Map are immutable, so you need to assign the return value of .updated, it will not change the original map.
If you want to change the map in place, you can use collection.mutable.Map and then
scala> val m = collection.mutable.Map(1 -> 2)
m: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> m.update(1, 3)
scala> m
res3: scala.collection.mutable.Map[Int,Int] = Map(1 -> 3)
If you want to set multiple values at once, you can do :
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val n = m ++ List((1 -> 3), (2 -> 4)) // also accepts an Array, a Map, …
n: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 4)

How to convert two consecutive elements from List to entries in Map?

I have a list:
List(1,2,3,4,5,6)
that I would like to to convert to the following map:
Map(1->2,3->4,5->6)
How can this be done?
Mostly resembles #Vakh answer, but with a nicer syntax:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map { case List(key, value) => key -> value}.toMap
// Map(1 -> 2, 3 -> 4, 5 -> 6)
Try:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map(l => (l(0), l(1))).toMap
if the list is guaranteed to be of even length:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map { x => x.head -> x.tail.head }.toMap
// Map(1 -> 2, 3 -> 4, 5 -> 6)
but if list may be of odd length, use headOption:
val l = List(1,2,3,4,5,6,7)
val m = l.grouped(2).map(x => x.head -> x.tail.headOption).toMap
// Map(1 -> Some(2), 3 -> Some(4), 5 -> Some(6), 7 -> None)
Without using grouped that appears ubiquitous in the answers so far.
scala> val l = (1 to 6).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> l.zip(l.tail).zipWithIndex.collect { case (e, pos) if pos % 2 == 0 => e }.toMap
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4, 5 -> 6)
You may also use sliding and foldLeft as follows:
scala> l.sliding(2,2).foldLeft(Map.empty[Int,Int]){ case (m, List(l, r)) => m + (l -> r) }
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4, 5 -> 6)

Scala updating/creating value in a hashmap

I don't understand this with Scala hasmaps:
How do I create a value or update one if it does not exist?
I am tryng to count the number of characters in a list of Strings.
I've tried this code but it doesn't work :
def times(chars: List[Char]): List[(Char, Int)] = {
val map = new HashMap[Char, Int]()
chars.foreach(
(c : Char) => {
map.update(c, map.get(c) + 1)
})
}
I understand the returning type isn't correct.
But is my foreach loop wrong?
Is there a prettier way to write it?
I think this will answer your question:
scala> "abaccdba".groupBy(identity).mapValues(_.length)
res3: scala.collection.immutable.Map[Char,Int] = Map(b -> 2, d -> 1, a -> 3, c -> 2)
Oh, and btw HashMap has a method getOrElseUpdate as to your original question
If someone wonder how to use GetOrElseUpdate and find this post here is the exemple I found :
val map = Map('a' -> 1, 'b' -> 2) //> map :
scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 2)
val newval = map.getOrElse('b', 0) + 1 //> newval : Int = 3
val updated = map + ('b' -> (newval)) //> updated :
scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 3)