【问题标题】:Count the elements of a list based to their type in Scala根据 Scala 中的类型计算列表的元素
【发布时间】:2017-04-21 13:17:25
【问题描述】:

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

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 = ???

谢谢

【问题讨论】:

    标签: scala


    【解决方案1】:

    您可以将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,如果你想要字符串)。

    【讨论】:

    • 我喜欢这个解决方案,但不是这样:val counts = fruits.groupBy{ case _:T => T }.map{ case (key, values) => (键,值。大小)}
    • 不,这行不通。您不能对抽象类型进行模式匹配。不过,您可能可以通过反射来做一些事情。
    【解决方案2】:

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

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

    上面将打印 2。只需更改其他示例的模式匹配即可。

    【讨论】:

    • 如果可能的话,我宁愿只遍历列表一次。
    【解决方案3】:

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

    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。

    【讨论】:

      【解决方案4】:

      您可以在groupBy 中使用getClass

      fruits.groupBy(f => f.getClass.getSimpleName).mapValues(_.size)
      
      // result:  Map(Apple$ -> 2, Pear$ -> 3, Orange -> 2)
      

      【讨论】:

      • 我喜欢这个,但美元符号有点可惜:-)
      • 请注意mapValues 是惰性的,因此它会在您每次请求时重新计算每个列表的大小。
      猜你喜欢
      • 2021-08-20
      • 1970-01-01
      • 2018-03-19
      • 2021-09-29
      • 1970-01-01
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      • 2017-09-13
      相关资源
      最近更新 更多