0

I have the following map of maps :

var map1 : mutable.Map[String, Map[String, Double]]
map1("U1" -> ("a"-> 2.1, "b"->3.21, "c"->7.1), 
     "U2" -> ("a"-> 3.1, "b"->12.1, "c"->1.4)
    )

I want to traverse through this map and merge all the values in a way that i get the following:

finalMap = ("a"->5.2, "b"->15.22, "c"->8.5)

I came across the this and imported scalaz for the solution but I'm a little unclear as to how to use the |+| on the values iterable.

I'm using the following code :

finalMap : Map[String, Double]()
map1.values.foreach{ (valueMap : Map[String, Double]) => 
  finalMap  |+| valueMap 
}

Any pointers on how to correct this

Community
  • 1
  • 1
hbabbar
  • 947
  • 4
  • 15
  • 33
  • "Any pointers on how to correct this" First, you need to tell us what needs correcting. What problem are you getting? (but for a started, you're merging each map with `finalMap` then throwing away the result, as `foreach` doesn't return a value) – The Archetypal Paul Jun 07 '16 at 07:11
  • @TheArchetypalPaul, I think he wants to sum maps values that have same key. And also his answer can be found here http://stackoverflow.com/questions/7076128/best-way-to-merge-two-maps-and-sum-the-values-of-same-key – Ionut Necula Jun 07 '16 at 07:16
  • @Ionut, yes that's obvious. What isn't obvious is "what needs correcting" as I said in my comment. We shouldn't be asked to work out what the symptoms are before suggesting a solution. – The Archetypal Paul Jun 07 '16 at 07:19
  • @TheArchetypalPaul Thanks for the comment, will keep that in mind for future. – hbabbar Jun 07 '16 at 09:26
  • @Ionut I have obviously researched on this answer and it has been linked in the question itself, my question was more on how to tweak the answer for my particular case – hbabbar Jun 07 '16 at 09:26

2 Answers2

3

To complement dhg's answer :

First, to use |+| on a Map[A, B] we need a Semigroup[B] instance. In this case Semigroup[Double]. But scalaz itself doesn't give you this instance, because it wouldn't follow the rules/laws of a correct Semigroup instance (related scalaz issue).

No despair, because we can still get an instance from the project scalaz-outlaws (which only supports scalaz 7.1.x it seems). Using the Double outlaw instance we can now combine two Doubles, while ignoring the deprecation message urging us not to use Monoid[Double] because it doesn't uphold the laws :

import scalaz.syntax.semigroup._
import scalaz.outlaws.std.double._

1.0 |+| 2.0
// <console>:18: warning: value doubleMonoid in trait DoubleOutlawInstances 
// is deprecated: Monoid[Double] violates the associativity law
//        1.0 |+| 2.0
//        ^
// res0: Double = 3.0

Second, we can simplify the reduce / fold with scalaz by using the Monoid[Double] implicitly instead of using |+| directly.

import scalaz.std.map._
import scalaz.outlaws.std.double._
import scalaz.syntax.foldable._

val map = Map("U1" -> Map("a"-> 2.1, "b"->3.21, "c"->7.1), 
              "U2" -> Map("a"-> 3.1, "b"->12.1, "c"->1.4))

map.concatenate
// Map[String,Double] = Map(a -> 5.2, b -> 15.309999999999999, c -> 8.5)

The map.concatenate is analogous to

map.values.fold(Monoid[Map[String, Double]].zero)(_ |+| _)
Peter Neyens
  • 9,770
  • 27
  • 33
  • I get the exception "object outlaws is not a member of package scalaz" even after changing my scalaz version to 7.1.8 (Also tried it on 7.1.1). Any particular version on which this should be available. – hbabbar Jun 07 '16 at 10:31
  • @hbabbar did you add the *scalaz-outlaws* dependency I linked to ? – Peter Neyens Jun 07 '16 at 11:17
  • 1
    How did I not know about `concatenate`? – oxbow_lakes Jun 07 '16 at 22:01
  • @PeterNeyens Is the concatenate method not present in scalaz-outlaws_2.10? – hbabbar Jul 14 '16 at 09:31
  • `concatenate` is scalaz itself in [`FoldableSyntax`](https://github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/syntax/FoldableSyntax.scala#L71). – Peter Neyens Jul 14 '16 at 10:46
  • @PeterNeyens Also, on using map.values.fold(Monoid[Map[String, Double]].zero)(_ |+| _), I get the exception "value |+| is not a member of Map[String,Double]" – hbabbar Jul 14 '16 at 11:46
  • Which version of scalaz are you using ? Does `ToFoldableOpsUnapply(map).concatenate` work ? Which imports are you using with the `fold` / `|+|` ? – Peter Neyens Jul 14 '16 at 12:05
  • I probably tested `map.concatenate` with the [si2712-fix compiler plugin](https://github.com/milessabin/si2712fix-plugin), without the plugin possible solutions are to use the `ToFoldableOpsUnapply` I showed above, or use a type alias : `type StringMap[A] = Map[String, A]` with then `(map: StringMap[StringMap[Double]]).concatenate` or `Foldable[StringMap].fold(map)` or concatenate the values :`map.values.toList.concatenate`. – Peter Neyens Jul 14 '16 at 12:25
  • @PeterNeyens I'm trying to use scalaz_outlaws_2.10 version 3. It is working on scalaz_outlaws_2.11 version 3 but for some spark build related issues we are forced to use 2.10 version. Also ToFoldableOpsUnapply(map).concatenate doesnt work with 2_10. I'm using the following imports: import scalaz.std.map._ import scalaz.outlaws.std.double._ import scalaz.syntax.foldable._ – hbabbar Jul 15 '16 at 03:27
1

First, use vals and immutable data structures. Second, look up how to initialize collections; there are many, many examples on the web.

val map1 = Map("U1" -> Map("a"-> 2.1, "b"->3.21, "c"->7.1), 
               "U2" -> Map("a"-> 3.1, "b"->12.1, "c"->1.4))

Finally, what you are trying to do is reduce all of the Maps in values into a single Map. You can do that like this:

val finalMap = map1.values.reduce(_ |+| _)
// finalMap: Map[String,Double] = Map(a -> 5.2, b -> 15.31, c -> 8.5)

If you think that map1 might be empty, then use fold instead, which is basically a reduce in which you give an initial value:

map1.values.fold(Map.empty)(_ |+| _)
// res0: Map[String,Double] = Map(a -> 5.2, b -> 15.31, c -> 8.5)

val emptyMap = Map.empty[String, Map[String, Double]]
emptyMap.values.fold(Map.empty)(_ |+| _)
// res1: Map[String,Double] = Map()
dhg
  • 52,383
  • 8
  • 123
  • 144
  • I made the desired changes of making it val and immutable and I'm trying to use reduce but i get the following exception :value |+| is not a member of Map[String,Double]. [This] (https://github.com/scalaz/scalaz/issues/766) here says "Double is not associative due to its hardware representation." What does this mean? – hbabbar Jun 07 '16 at 09:21
  • @hbabbar It means that because of how `Double` are represented `(a +b) + c` is not always equal to `a + (b + c)`. For a workaround see my answer with some additions to this answer. – Peter Neyens Jun 07 '16 at 09:26