Reputation: 12107
I am writing a simple function called reduceByKey
that takes a collection of (key, numeric) pairs and returns reduced collection by key.
def reduceByKey[K](collection: Traversable[Tuple2[K, Int]]) = {
collection
.groupBy(_._1)
.map { case (group: K, traversable) => traversable.reduce{(a,b) => (a._1, a._2 + b._2)} }
}
This currently works for:
scala> val col = List((("some","key"),100), (("some","key"),100), (("some","other","key"),50))
col: List[(Product with Serializable, Int)] = List(((some,key),100), ((some,key),100), ((some,other,key),50))
scala> reduceByKey(col)
res42: scala.collection.immutable.Map[Product with Serializable,Int] = Map((some,key) -> 200, (some,other,key) -> 50)
But, I as soon as I want to use non-Int type for numeric, it fails miserably, as it expects an Int
.
scala> val col = List((("some","key"),100.toDouble), (("some","key"),100.toDouble), (("some","other","key"),50.toDouble))
col: List[(Product with Serializable, Double)] = List(((some,key),100.0), ((some,key),100.0), ((some,other,key),50.0))
scala> reduceByKey(col)
<console>:13: error: type mismatch;
found : List[(Product with Serializable, Double)]
required: Traversable[(?, Int)]
reduceByKey(col)
^
Of course, I could make different methods for different types, but that would be silly. Basically I want my method to work with any type that has +
method defined. That would be Double
, Float
, Long
, Int
and Short
.
Numeric.plus
.I am open to any suggestions as how to solve this.
Upvotes: 7
Views: 14366
Reputation: 32719
If you are only interested in numeric values, you can use the standard Numeric
type class and do this:
def reduceByKey[K,V](collection: Traversable[Tuple2[K, V]])(implicit num: Numeric[V]) = {
import num._
collection
.groupBy(_._1)
.map { case (group: K, traversable) => traversable.reduce{(a,b) => (a._1, a._2 + b._2)} }
}
The num
implicit parameter serves as an evidence that V
is a numeric type, and provides the +
operation for this type.
Upvotes: 15