根据Scala中的类型计算列表的元素

时间:2017-04-21 13:17:25

标签: scala

我想根据他们的类型得到每种水果的数量。显然我需要以某种方式积累它们,但最好/最干净/等等是什么?

trait Fruit
case object Apple extends Fruit
case object Pear extends Fruit
case class Orange(variety: String) extends Fruit

val fruits = List(Pear, Apple, Pear, Pear, Apple, Orange("satsuma"), Orange("clementine"))

val numberOfOranges = ???
val numberOfApples = ???
val numberOfPears = ???

由于

4 个答案:

答案 0 :(得分:4)

您可以将groupBy与模式匹配结合使用:

val counts = fruits.groupBy{
  case Apple => "apple"
  case Pear => "pear"
  case Orange(_) => "orange"
}.map{
  case (key, values) => (key, values.size)
}

编辑如果您喜欢反射,而您的子类型没有类型参数,则可以使用fruits.groupBy(_.getClass)(或getClass.getSimpleName,如果您需要字符串)。

答案 1 :(得分:2)

您可以使用count模式匹配来执行此操作:

val numberOfOranges = fruits.count { case Orange(_) => true
                                     case _ => false }

以上将打印2.只需更改其他示例的模式匹配。

答案 2 :(得分:2)

这是一个使用反射的解决方案,尽管您需要使用模式匹配来使范围内的类型。您不能一般地迭代列表并获取每个元素的类型。

val orangeType = reflect.runtime.universe.typeOf[Orange]
val appleType = reflect.runtime.universe.typeOf[Apple.type]
val pearType = reflect.runtime.universe.typeOf[Pear.type]

def getType[T: reflect.runtime.universe.TypeTag](obj: T) =
  reflect.runtime.universe.typeOf[T]

def typesOf[A](fs: List[A]): List[reflect.runtime.universe.Type] = {
  fs.map {
    // Use pattern match to reify type
    case v @ Apple => getType(v)
    case v @ Pear => getType(v)
    case v @ Orange(_) => getType(v)
  }
}

val fruitCount = typesOf(fruits).groupBy(identity).mapValues(_.size)

val numberOfOranges = fruitCount(orangeType)
val numberOfApples = fruitCount(appleType)
val numberOfPears = fruitCount(pearType)

老实说,这只是通过Scala反射类型进行分组,而不是像字符串(或其他一些原始类型)更明显的东西,并且很快变得过于复杂。最好的长期编码解决方案是提出自己的枚举并使用它,IMO。

答案 3 :(得分:1)

您可以在getClass

中使用groupBy
fruits.groupBy(f => f.getClass.getSimpleName).mapValues(_.size)

// result:  Map(Apple$ -> 2, Pear$ -> 3, Orange -> 2)