Reputation: 51113
Given a heterogeneous sequence, how can I pull out only members of a certain type, and operate on those members in a type-safe way?
If I have:
trait Foo
case class Bar(baz: String)
case class Qux(bar: Bar, quux: String) extends Foo
case class Corge(grault: Int) extends Foo
If I want to take this sequence of mixed Corges
and Quxes
...
val s = Seq[Foo](Corge(1), Qux(Bar("2"), "3"), Qux(Bar("4"), "5"), Corge(6), Qux(Bar("2"), "7"))
...and pull out just the Quxes
, grouped by Bar
:
Map(
Bar(2) -> List(Qux(Bar(2),3), Qux(Bar(2),7)),
Bar(4) -> List(Qux(Bar(4),5))
)
I can do this:
s filter { f => f.isInstanceOf[Qux] } groupBy {
f => f.asInstanceOf[Qux].bar }
Or I can do this:
(s.filter({ f => f.isInstanceOf[Qux] }).asInstanceOf[Seq[Qux]]) groupBy {
q => q.bar }
But either way I need two instanceOf
checks, when it seems like I should be able to get away with one. Or none. Is there some clever pattern-matching solution I'm missing?
Upvotes: 3
Views: 762
Reputation: 26486
Even better than Daniel Martin's solution is to use 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)
Upvotes: 14
Reputation: 23548
No explicit instanceOf
needed, just the implicit kind you get with using a class in a 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)
Note the types of a
and b
.
Upvotes: 1