hezamu
hezamu

Reputation: 1584

Filtering a Scala List by type

I have a class structure like this

abstract class A
class B extends A
class C extends A
class D extends A
class E extends A

and I have a collection of the various instances, for example:

val xs = List(new D, new B, new E, new E, new C, new B)

My question is, is there an elegant way to filter out some of the subclasses from the List?

Let's say I want all instances except B's and C's. I can do it with a bunch of isInstanceOf's, or using collect like this:

val ys = (xs collect {
    case b: B => None
    case c: C => None
    case notBorC => notBorC
}).filter(_ != None).asInstanceOf[List[A]]

This works but it feels awkward, mostly because of the filter and cast. Is there a more elegant way? Less code is preferred, and I'd like to have a solution that doesn't need to be updated if I add more subclasses of A.

Upvotes: 58

Views: 21591

Answers (3)

Grozz
Grozz

Reputation: 8425

collect can be used to filter values on which the function is defined:

Get all values of type A:

xs.collect { case a: A => a }

Get all values except B and C:

xs diff xs.collect { case x@(_: B | _: C) => x }

Upvotes: 112

Travis Brown
Travis Brown

Reputation: 139028

flatMap that shit! (as they say):

scala> val ys = xs flatMap {
     |   case _: B | _: C => None
     |   case other => Some(other)
     | }
ys: List[A] = List(D@7ecdc97b, E@2ce07e6b, E@468bb9d1)

In your case you were getting a List[ScalaObject] because ScalaObject is the least upper bound of None, D, and E.

Upvotes: 55

hezamu
hezamu

Reputation: 1584

Formulating problems as questions seems to be a pretty good way of solving them :) My question actually provides the answer - just filter by subtype:

val ys = xs filterNot(List(classOf[B], classOf[C]) contains _.getClass)

Upvotes: 4

Related Questions