2014-03-05 32 views
1

鑑於異構序列,我如何才能僅抽取某種類型的成員,並以類型安全的方式對這些成員進行操作?Scala:在一個操作中過濾和投射

如果我有:

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檢查,當它看起來像我應該能夠與一個脫身。或者沒有。是否有一些我缺少的巧妙模式匹配解決方案?

回答

11

甚至比丹尼爾·馬丁的更好的解決方案是使用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

不需要明確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