Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala reduceByKey function - use any type that has + method

Tags:

scala

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.

  1. At first, I thought I could use structural type instead of Int. But that would mean the structural type would need to reference itself in order to be of any use.
  2. I looked into Numeric trait that I think could be useful. It encapsulates the + methods of all numeric types. However, I am not sure how to use it in my case. I dont want force the user of my function to wrap values in Numeric just for my function to work. The function itself should somehow implicitly wrap it up and invoke Numeric.plus.

I am open to any suggestions as how to solve this.

like image 373
Grega Kešpret Avatar asked Apr 12 '13 15:04

Grega Kešpret


1 Answers

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.

like image 102
Régis Jean-Gilles Avatar answered Nov 09 '22 14:11

Régis Jean-Gilles