2014-09-02 38 views
5
val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = data.filter(datum => selection.contains(datum.MYINDEX)) 
//             INVALID CODE HERE^
// selectedData: List("foo", "bash") 

Angenommen, ich möchte eine List mit einer Liste ausgewählter Indizes filtern. Wenn ich in der filter Methode den Index eines Listenelements referenzieren könnte, könnte ich das wie oben lösen, aber datum.MYINDEX ist im obigen Fall nicht gültig.Eine Liste nach Artikelindex filtern?

Wie könnte ich das stattdessen tun?

Antwort

6

Wie wäre es mit zipWithIndex, um einen Verweis auf den Index des Artikels, Filterung als solche, dann Mapping der Index weg?

data.zipWithIndex 
    .filter{ case (datum, index) => selection.contains(index) } 
    .map(_._1) 
+0

einen letzten Punkt (nach tatsächlich das Lesen der Link, den Sie gab) ist, dass Ihr Angebot Bergbau - die Bracketing Debatte die uralte ägyptische Bracketing Argument war, die in Sprachen wie Java * optional * (in Scala es nicht ist). Jetzt bin ich mir keiner Sprache bewusst, in der es OK ist, eine Aussage/einen Ausdruck auf die * selbe Zeile * als die öffnende Klammer zu setzen, das ist objektiv falsch, NICHT persönliche Präferenz. – samthebest

+0

Die einzige Sache, die ich einräume, ist, dass die Anleitung ein wenig implizit ist, und das Leerzeichen vor geschweiften Klammern, und nur explizit besagt, dass schließende geschweifte Klammern in einer eigenen Zeile sein sollten. Die schließende Klammer befindet sich in einer eigenen Zeile unmittelbar darauf die letzte Zeile der Funktion ". Deshalb bearbeite ich ein letztes Mal, entsprechend dem expliziten, objektiv wahren Teil des Styleguides, und überlasse alle impliziten Fehler dir. – samthebest

+0

@samthebest, gibt es viele verschiedene Formatierungswerkzeuge und Einstellungen. Warum bestehen Sie darauf, einen bestimmten zu verwenden? – Basilevs

0

Erste Lösung, die mir in den Sinn kam, war jedes Element eine Liste von Paaren (Element, Index), filtern, indem die Überprüfung zu erstellen, wenn die Indexauswahl enthält, dann (Karte resultierende Liste, um nur roh elementd zu halten Index weglassen). Code ist selbsterklärend:

data.zipWithIndex.filter(pair => selection.contains(pair._2)).map(_._1) 

oder besser lesbar.

val elemsWithIndices = data.zipWithIndex 
val filteredPairs = elemsWithIndices.filter(pair => selection.contains(pair._2)) 
val selectedElements = filteredPairs.map(_._1) 
+0

Vielleicht ist die zweite lesbarer für jemanden mit 2 Tagen Scala Erfahrung, aber der erste Weg ist so idiomatische die meisten Scala Devs würde es für seine Eleganz bevorzugen. – samthebest

1

Es ist ordentlicheres es anders zu tun, um (wenn auch möglicherweise langsam mit Listen als Indizierung langsam ist (O (n)) Vektoren wäre besser. auf der anderen Seite ist die contains der anderen Lösung für jedes Element in data nicht genau schnell)

val data = List("foo", "bar", "bash") 
     //> data : List[String] = List(foo, bar, bash) 
val selection = List(0, 2) 
     //> selection : List[Int] = List(0, 2) 
selection.map(index=>data(index)) 
     //> res0: List[String] = List(foo, bash) 
+0

Dies wird eine Ausnahme auslösen, wenn Sie einen Index haben, der größer ist als die Größe der Datenliste. –

+0

Ja. Das OP kann sagen, ob das ein Problem ist und wie das Verhalten für einen ungültigen Index aussehen sollte. –

+0

Das Auslösen von Ausnahmen ist jedoch im Allgemeinen kein gutes Verhalten. –

0

Da Sie eine Liste von Indizes alread y, der effizienteste Weg ist, diese Indizes direkt zu holen:

val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = selection.map(index => data(index)) 

oder sogar:

val selectedData = selection.map(data) 

oder wenn Sie die Reihenfolge der Elemente in Daten zu erhalten:

val selectedData = selection.sorted.map(data) 

AKTUALISIERT

Im Sinne der Suche nach allen möglichen e Algorithmen, hier ist die Version mit collect:

val selectedData = data 
    .zipWithIndex 
    .collect { 
    case (item, index) if selection.contains(index) => item 
    } 
+1

Dies wird eine Ausnahme auslösen, wenn Sie einen Index haben, der größer ist als die 'data' Liste. –

+0

Ja, guter Punkt. –

+0

Aber vielleicht ist eine Ausnahme das erforderliche Verhalten. Genauso wie ein einfacher einzelner Index der Liste mit einem übergroßen Index. –

0

Dieses Werk:

val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = data.filter(datum => selection.contains(data.indexOf(datum))) 
println (selectedData) 

Ausgabe: List (foo, bash)

0

Das Folgende ist die wohl am besten skalierbare Art und Weise, es zu tun in Bezug auf Effizienz, und im Gegensatz zu vielen Antworten auf SO, tatsächlich folgt die offizielle Scala Style Guide genau.

import scala.collection.immutable.HashSet 

val selectionSet = new HashSet() ++ selection 

data.zipWithIndex.collect { 
    case (datum, index) if selectionSet.contains(index) => datum 
} 

Wenn die resultierende Sammlung an zusätzlichen map, weitergegeben werden flatMap, etc, schlagen data in einen faulen Sequenz drehen. Eigentlich sollte man das trotzdem machen, um 2-Pässe zu vermeiden, einen für die zipWithIndex einen für die collect, aber ich bezweifle, dass man beim Benchmarking viel gewinnen würde.