2012-11-14 15 views
26

ho una struttura di classe come questoFiltrare un elenco Scala per tipo

e ho una collezione dei diversi casi, ad esempio:

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

La mia domanda è, c'è un modo elegante per filtrare alcune sottoclassi dalla lista?

Diciamo che voglio tutte le istanze tranne B e C. Posso farlo con un po 'di isInstanceOf di, o utilizzando raccogliere in questo modo:

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

Questo funziona ma ci si sente a disagio, soprattutto a causa del filtro e cast. C'è un modo più elegante? Si preferisce meno codice e mi piacerebbe avere una soluzione che non debba essere aggiornata se aggiungo più sottoclassi di A.

risposta

34

flatMap quella merda! (as they say):

scala> val ys = xs flatMap { 
    | case _: B | _: C => None 
    | case other => Some(other) 
    | } 
ys: List[A] = List([email protected], [email protected], [email protected]) 

Nel tuo caso si stavano diventando un List[ScalaObject] perché ScalaObject è l'estremo superiore di None, D e E.

+0

Sì, questa è una soluzione molto migliore del mio primo tentativo. – hezamu

3

Formulare problemi come domande sembra essere un buon modo per risolverli :) la mia domanda in realtà fornisce la risposta - basta filtrare per sottotipo:

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

La tua intenzione dovrebbe essere che '' ys'' è la lista vuota se i tuoi elenchi di esclusione contengono '' classOf [A] '', allora avrai bisogno di qualcosa di diverso. Ad esempio: '' xs.filterNot (x => List (classOf [A]). Exists (y => y.isInstance (x))) ''. La soluzione di Travis esibisce anche questa proprietà. –

+0

@mhs: è vero, anche se nel mio caso A è astratto quindi averlo nella lista di esclusione non è rilevante. Ad ogni modo, sono d'accordo che la soluzione flatMap di Travis sia superiore anche a questa. – hezamu

+0

No, non sono d'accordo che la soluzione di travis sia necessariamente superiore alla soluzione. I tuoi potrebbero essere astratti nell'insieme di classi che vorresti filtrare, la soluzione di Travis non potrebbe essere sottratta in quella direzione. Ma tu usi un set invece di una lista nell'argomento filterNot. – jmg

45

collect può essere utilizzato per filtrare i valori su cui è definita la funzione:

garantita tutti i valori di tipo A:

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

ottenere tutti i valori eccetto B e C:

xs diff xs.collect { case [email protected](_: B | _: C) => x }