开发者

What is the Scala syntax for summing a List of objects?

开发者 https://www.devze.com 2023-04-08 17:32 出处:网络
For example case class Blah(security: String, price: Double) val myList = List(Blah(\"a\", 2.0), Blah(\"b\", 4.0))

For example

case class Blah(security: String, price: Double)
val myList = List(Blah("a", 2.0), Blah("b", 4.0))
val sum = myList.sum(_.price) /开发者_如何学C/ does not work

What is the syntax for obtaining the sum?


Try this:

val sum = myList.map(_.price).sum

Or alternately:

val sum = myList.foldLeft(0.0)(_ + _.price)

You appear to be trying to use this method:

def sum [B >: A] (implicit num: Numeric[B]): B

and the compiler can't figure out how the function you're providing is an instance of Numeric, because it isn't.


Scalaz has this method under a name foldMap. The signature is:

def M[A].foldMap[B](f: A => B)(implicit f: Foldable[M], m: Monoid[B]): B

Usage:

scala> case class Blah(security: String, price: Double)
defined class Blah

scala> val myList = List(Blah("a", 2.0), Blah("b", 4.0))
myList: List[Blah] = List(Blah(a,2.0), Blah(b,4.0))

scala> myList.foldMap(_.price)
res11: Double = 6.0

B here doesn't have to be a numeric type. It can be any monoid. Example:

scala> myList.foldMap(_.security)
res12: String = ab


As an alternative to missingfaktor's Scalaz example, if you really want to sum a list of objects (as opposed to mapping each of them to a number and then summing those numbers), scalaz supports this as well.

This depends on the class in question having an instance of Monoid defined for it (which in practice means that it must have a Zero and a Semigroup defined). The monoid can be considered a weaker generalisation of core scala's Numeric trait specifically for summing; after all, if you can define a zero element and a way to add/combine two elements, then you have everything you need to get the sum of multiple objects.

Scalaz' logic is exactly the same as the way you'd sum integers manually - list.foldLeft(0) { _ + _ } - except that the Zero provides the initial zero element, and the Semigroup provides the implementation of + (called append).

It might look something like this:

import scalaz._
import Scalaz._

// Define Monoid for Blah
object Blah {
  implicit def zero4Blah: Zero[Blah] = zero(Blah("", 0))
  implicit def semigroup4Blah: Semigroup[Blah] = semigroup { (a, b) => 
    // Decide how to combine security names - just append them here
    Blah(a.security + b.security, a.price + b.price)
  }
}

// Now later in your class
val myList = List(Blah("a", 2.0), Blah("b", 4.0))
val mySum = myList.asMA.sum

In this case mySum will actually be an instance of Blah equal to Blah("ab", 6.0), rather than just being a Double.

OK, for this particular example you don't really gain that much because getting a "sum" of the security names isn't very useful. But for other classes (e.g. if you had a quantity as well as a price, or multiple relevant properties) this can be very useful. Fundamentally it's great that if you can define some way of adding two instances of your class together, you can tell scalaz about it (by defining a Semigroup); and if you can define a zero element too, you can use that definition to easily sum collections of your class.

0

精彩评论

暂无评论...
验证码 换一张
取 消