How to add all the values of a map without using recurrsion or var - scala

I want to add all the values in a map without using var or any mutable structures. I have tried to do something like this but it doens't work:
val mymap = ("a" -> 1, "b" -> 2)
val sum_of_alcohol_consumption =
for ((k,v) <- mymap ) yield (sum_of_alcohol_consumption += v)
I have been told that I can use .sum on a list
Please help
Thanks

You can use the .values function of a Map to return an Iterable List of its values (all of the Integers) and then call the .sum function on that:
val myMap = Map("a" -> 1, "b" -> 2)
val sum = myMap.values.sum
println(sum) // Outputs: 3

An equivalent answer to the more elegant use of sum is to use a fold operation. sum is implemented in a manner similar to this:
val myMap = Map("a" -> 1, "b" -> 2)
val sumAlcoholConsumption = myMap.values.foldLeft(0)(_ + _)
values returns a sequence of only the values in the map. The first foldLeft argument is the zero value (think of it as the initial value for an accumulator value) for the operation. The second argument is a function that adds the current value of the accumulator to the current element, returning the sum of the two values - and it is applied to each value in turn. That said, sum is a lot more convenient.

To get the only values of map, it provides a function values which will return iterable,we can directly appy sum function to it.
scala> val mymap = Map("a" -> 1, "b" -> 2)
mymap: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
scala> mymap.values.sum
res7: Int = 3

Related

Scala: for-loop with getOrElse doesn't work?

I want to find the values to a list of keys in a map. If the map doesn't contain the key, it should return an empty List.
val keys = List("a", "b", "c")
val kv = Map("a" -> List(0,1), "b" -> List(1))
val result: List[List[Int]] = List( List(0,1), List(1), List())
My approach was the following:
val result = for (key <- keys; value <- kv.getOrElse(key, List.empty[Int])) yield value
But that just returns result : List[Int] = List(0, 1, 1)
And kv.filterKeys(word => keys.contains(word)).values.toList doesn't return the empty list either (obviously)
So my question is: Why does the getOrElse function not seem to work properly or how could I do it better?
You are doing the equivalent of a flatMap, so the empty list is getting returned, it's just being flattened out. You want to just use a map:
keys map {kv.getOrElse(_, List())}
If you want to stick to using for-yield, just change the value generator to an assignment:
for (key <- keys; value = kv.getOrElse(key, List.empty[Int])) yield value
which is equivalent to:
for (key <- keys) yield kv.getOrElse(key, List.empty[Int])
For more details about for-yield, here's a Scala doc.

Scala - reducing a map based on changed key

Lets say have the following Map data
val testMap: Map[String, Int] = Map("AAA_abc" -> 1,
"AAA_anghesh" -> 2,
"BBB_wfejw" -> 3,
"BBB_qgqwe" -> 4,
"C_fkee" -> 5)
Now I want to reduce the map by key.split("_").head and add all the values for the keys that became equal. So for this example the Map should result into:
Map(AAA -> 3, BBB -> 7, C -> 5)
What would be the correct way to do so in Scala?
I tried constructions with groupBy and reduceLeft but could not find a solution.
Here's a way to do it:
testMap.groupBy(_._1.split("_").head).mapValues(_.values.sum)
A variation in one pass:
testMap.foldLeft(Map[String,Int]())( (map, kv) => {
val key = kv._1.split("_").head
val previous = map.getOrElse(key,0)
map.updated(key, previous + kv._2) })

Sum of Values based on key in scala

I am new to scala I have List of Integers
val list = List((1,2,3),(2,3,4),(1,2,3))
val sum = list.groupBy(_._1).mapValues(_.map(_._2)).sum
val sum2 = list.groupBy(_._1).mapValues(_.map(_._3)).sum
How to perform N values I tried above but its not good way how to sum N values based on key
Also I have tried like this
val sum =list.groupBy(_._1).values.sum => error
val sum =list.groupBy(_._1).mapvalues(_.map(_._2).sum (_._3).sum) error
It's easier to convert these tuples to List[Int] with shapeless and then work with them. Your tuples are actually more like lists anyways. Also, as a bonus, you don't need to change your code at all for lists of Tuple4, Tuple5, etc.
import shapeless._, syntax.std.tuple._
val list = List((1,2,3),(2,3,4),(1,2,3))
list.map(_.toList) // convert tuples to list
.groupBy(_.head) // group by first element of list
.mapValues(_.map(_.tail).map(_.sum).sum) // sums elements of all tails
Result is Map(2 -> 7, 1 -> 10).
val sum = list.groupBy(_._1).map(i => (i._1, i._2.map(j => j._1 + j._2 + j._3).sum))
> sum: scala.collection.immutable.Map[Int,Int] = Map(2 -> 9, 1 -> 12)
Since tuple can't type safe convert to List, need to specify add one by one as j._1 + j._2 + j._3.
using the first element in the tuple as the key and the remaining elements as what you need you could do something like this:
val list = List((1,2,3),(2,3,4),(1,2,3))
list: List[(Int, Int, Int)] = List((1, 2, 3), (2, 3, 4), (1, 2, 3))
val sum = list.groupBy(_._1).map { case (k, v) => (k -> v.flatMap(_.productIterator.toList.drop(1).map(_.asInstanceOf[Int])).sum) }
sum: Map[Int, Int] = Map(2 -> 7, 1 -> 10)
i know its a bit dirty to do asInstanceOf[Int] but when you do .productIterator you get a Iterator of Any
this will work for any tuple size

Fold from Map[String,List[Int]] to Map[String,Int]

I'm fairly new to Scala and functional approaches in general. I have a Map that looks something like this:
val myMap: Map[String, List[Int]]
I want to end up something that maps the key to the total of the associated list:
val totalsMap: Map[String, Int]
My initial hunch was to use a for comprehension:
val totalsMap = for (kvPair <- myMap) {
kvPair._2.foldLeft(0)(_+_)
}
But I have no idea what I would put in the yield() clause in order to get a map out of the for comprehension.
You can use mapValues for this,
val totalMap = myMap.mapValues(_.sum)
But mapValues will recalculate the sum every time you get a key from the Map. e.g. If you do totalMap("a") multiple times, it will recalculate the sum each time.
If you don't want this, you should use
val totalMap = myMap map {
case (k, v) => k -> v.sum
}
mapValues would be more suited for this case:
val m = Map[String, List[Int]]("a" -> List(1,2,3), "b" -> List(4,5,6))
m.mapValues(_.foldLeft(0)(_+_))
res1: scala.collection.immutable.Map[String,Int] = Map(a -> 6, b -> 15)
Or without foldLeft:
m.mapValues(_.sum)
val m = Map("hello" -> Seq(1, 1, 1, 1), "world" -> Seq(1, 1))
for ((k, v) <- m) yield (k, v.sum)
yields
Map(hello -> 4, world -> 2)`
The for comprehension will return whatever monadic type you give it. In this case, m is a Map, so that's what's going to come out. The yield must return a tuple. The first element (which becomes the key in each Map entry) is the word you're counting, and the second element (you guessed it, the value in each Map entry) becomes the sum of the original sequence of counts.

Select first 'N' elements from map in Scala

Is there an elegant method of extracting first 'N' elements from a Map ?
I could create a new Map and iterate over the values that are to be selected, is there a function that accomplishes this ?
From the docs for the take method on Map:
Selects first n elements.
Note: might return different results for different runs, unless the
underlying collection type is ordered.
In the case of maps the collection isn't ordered, so don't count on getting the first n elements—in fact the concept of the first n elements doesn't even exist for maps.
But take will give you some first n elements, and it sounds like this is what you want:
scala> Map('a -> 1, 'b -> 2, 'c -> 3).take(2)
res1: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2)
In this case you happen to get the two elements that came first in the definition, but don't count on this happening.
Sounds like you're looking for a SortedMap, along with take(n) as discussed by others.
scala> val map = Map[String,Int]("one"->1,"two"->2,"three"->3)
map: scala.collection.immutable.Map[String,Int] =
Map(one -> 1, two -> 2, three -> 3)
scala> val n = 2
n: Int = 2
scala> val firstN = map.take(n)
firstN: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2)