首页 > 解决方案 > collect 方法何时以及为什么无法按子类型过滤 Scala 集合?

问题描述

与按类型过滤 Scala 列表相关,我正在尝试按值的类型过滤 Map,但我发现使用该collect方法的解决方案会产生类型错误。

例如,考虑

abstract class A
class B extends A
class C extends A

val collection: Map[String, A] = Map(("One", new B), ("Two", new B), 
  ("Three", new C), ("Four", new C))

val filtered: Map[String, B] = collection.collect { 
  case x@(_: String, _: B) => x }

这会产生一个编译错误,说明在需要Map[String, A]时找到Map[String, B]。PartialFunction 参数的重点不是collect仅在某些值上定义吗?

但是,此方法适用于列表(如链接的帖子中所示):

val collection: List[A] = List(new B, new B, new C, new C)

val filtered: List[B] = collection.collect { case x: B => x }

但是,经过进一步的实验,我发现它也不适用于 Lists of Lists。这会产生C无法转换为的运行时错误B

val collection: List[List[A]] = List(List(new B), List(new B), 
  List(new C), List(new C))

val filtered: List[List[B]] = collection.collect { case x: List[B] => x }

这里发生了什么?我错过了很多事情吗?


作为旁注,给出的解决方案之一确实有效:

val collection: Map[String, A] = Map(("One", new B), ("Two", new B), 
  ("Three", new C), ("Four", new C))

val filtered = collection.flatMap {
  case x@(_: String, _: B) => Some(x)
  case _ => None
}

标签: scala

解决方案


问题是这x是一个类型的变量(String, A)@返回成功的元素unapply,也就是地图的原始元素(String, A)

解决它的一种方法是使用从模式匹配中提取的值创建一个新元组(因为根据定义它们是 String 和 B)。

val collection: Map[String, A] = Map(("One", new B), ("Two", new B), ("Three", new C), ("Four", new C))

val filtered:Map[String, B] = collection.collect {
  case (k: String, v: B) => (k, v)
}

filtered.foreach(println)

推荐阅读