Reputation: 1450
I'm trying to create a fly-by-seat-of-pants sparse vector library in scala and I'm hitting an issue with foldLeft where it seems to be creating or adding an extra element on sequences of length one.
Here's my sparse addition function:
def addTwoMaps(m1: Map[Int,Double], m2: Map[Int,Double]) =
m1 ++ m2.map{ case (k,v) => k -> (v + m2.getOrElse(k, 0.)) }
And here's my "add over sequence of maps/sparse vectors and normalize" function:
def addNMaps(ms : Map[Int, Double]*) = {
val denom = if (ms.length > 0) ms.length.toDouble else 1
ms.foldLeft(Map.empty[Int, Double])((a,b) => addTwoMaps(a,b)).mapValues(_ / denom)
}
(for my particular case, the values of each input map sum to one so all I have to do is divide by the length of the argument sequence to make sure the resulting map sums to one over its values)
As a test case, if I add two maps which have values that sum to one, it works out fine:
scala> Common.addNMaps(Map(1->1), Map(1->1))
res34: scala.collection.immutable.Map[Int,Double] = Map(1 -> 1.0)
But if I have only one argument:
scala> Common.addNMaps(Map(1->1))
res33: scala.collection.immutable.Map[Int,Double] = Map(1 -> 2.0)
The values sum to two all of a sudden! My guess is that the single Map(1->1)
is being added twice somehow in foldLeft
but it's only a guess.
What am I doing wrong? And how can I get Common.addNMaps(Map(1->1))
to return Map(1->1.0)
?
Upvotes: 1
Views: 757
Reputation: 139038
There's a typo in your addTwoMaps
, which should be:
def addTwoMaps(m1: Map[Int,Double], m2: Map[Int,Double]) =
m1 ++ m2.map{ case (k,v) => k -> (v + m1.getOrElse(k, 0.)) }
You need to call getOrElse
on m1
, not m2
.
Note that in this case you can use an IntMap
, which has a handy unionWith
method (presumably inspired by Haskell's Data.Map.unionWith
):
import scala.collection.immutable.IntMap
def addNMaps(ms : IntMap[Double]*) = {
val denom = if (ms.length > 0) ms.length else 1
ms.foldLeft(IntMap.empty[Double]) {
(a, b) => a.unionWith(b, (_, x, y) => x + y)
}.mapValues(_ / denom)
}
I'm not sure why unionWith
isn't part of the standard Scala Map
API.
Upvotes: 1