Scala:在一次操作中过滤和转换

时间:2014-03-05 22:36:44

标签: scala collections

鉴于异构序列,我如何只提取某种类型的成员,并以类型安全的方式对这些成员进行操作?

如果我有:

trait Foo
case class Bar(baz: String)
case class Qux(bar: Bar, quux: String) extends Foo
case class Corge(grault: Int) extends Foo

如果我想采用混合CorgesQuxes ...

这一序列
val s = Seq[Foo](Corge(1), Qux(Bar("2"), "3"), Qux(Bar("4"), "5"), Corge(6), Qux(Bar("2"), "7"))

...并提取Quxes,按Bar分组:

Map(
  Bar(2) -> List(Qux(Bar(2),3), Qux(Bar(2),7)), 
  Bar(4) -> List(Qux(Bar(4),5))
)

我可以这样做:

s filter { f => f.isInstanceOf[Qux] } groupBy { 
    f => f.asInstanceOf[Qux].bar }

或者我可以这样做:

(s.filter({ f => f.isInstanceOf[Qux] }).asInstanceOf[Seq[Qux]]) groupBy { 
    q => q.bar }

但无论哪种方式,我需要进行两次instanceOf次检查,当我看起来应该可以逃脱一次。或者没有。是否有一些我缺少的聪明的模式匹配解决方案?

2 个答案:

答案 0 :(得分:12)

比丹尼尔·马丁的解决方案更好的是使用collect

val a: Seq[Any] = List(1, 2, "asdf", 4)
a: Seq[Any] = List(1, 2, asdf, 4)

val b = a collect { case s: String => s }
b: Seq[String] = List(asdf)

答案 1 :(得分:1)

不需要明确的instanceOf,只是使用case中的类所隐含的类型:

$ scala
Welcome to Scala version 2.9.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val a : Seq[Any] = List(1, 2, "asdf", 4)
a: Seq[Any] = List(1, 2, asdf, 4)

scala> val b = a flatMap {i:Any => i match { case s:String => Some(s); case _ => None }}
b: Seq[String] = List(asdf)

请注意ab的类型。