2012-09-12 16 views
12

Angenommen, ich habe die folgende Liste von Tupeln konvertieren:Scala: Wie Tupelelemente auf Listen

val tuples = listOfStrings.map(string => { 
      val split = string.split(":") 
      (split(0), split(1), split(2)) 
     }) 

Ich mochte das Split (0) in einer Liste, Split (1) in einer anderen Liste erhalten und bald. Ein einfacher Weg, dies erreicht werden könnte, ist durch Schreiben:

list1 = tuples.map(x => x._1).toList 
list2 = tuples.map(x => x._2).toList 
list3 = tuples.map(x => x._3).toList 

Gibt es eine elegantere (funktionaler) Weg, um die oben erreichen, ohne 3 separate Aussagen zu schreiben?

Antwort

11

Dies wird Ihnen Ihr Ergebnis als eine Liste der Liste:

tuples.map{t => List(t._1, t._2, t._3)}.transpose 

Wenn Sie sie in lokalen Variablen speichern möchten, gerade tun:

val List(l1,l2,l3) = tuples.map{t => List(t._1, t._2, t._3)}.transpose 

UPDATE: Wie von Blaisorblade, die Standard-Bibliothek tatsächlich gezeigt verfügt über eine integrierte Methode für diese: unzip3, die wie unzip aber für Tripel statt Paare ist:

val (l1, l2, l3) = tuples.unzip3 

Unnötig zu sagen, sollten Sie diese Methode über meine handgerollte Lösung oben (aber für Tupel begünstigen von Arity> 3 würde dies immer noch gelten).

+0

zu langsam :) etwa war schreiben: 'tuples.map {t => t._1 :: t._2 :: t._3 :: Nil} .transpose 'nice one. +1 –

+0

Schlechte Idee, sollten Sie entpacken von der anderen Antwort: http://stackoverflow.com/a/17281359/53974 – Blaisorblade

+0

@Balisorblade: Ich muss nicht zustimmen. Dies wäre wahr, wenn es ein 'Tuple2' wäre, aber es ist ein' Tuple3'. 'unzip' behandelt nur entpackende Paare, und das eigene Beispiel von jeschan zeigt eine Liste von' Tuple3', nicht 'Tuple2'. –

1

Sie könnten einfach die Anweisungen in einer einzigen Zeile schreiben.

Wie

(list1, list2, list3) = tuples.foldRight((List[String](), List[String](), List[String]()))((a,b) => (a._1 :: b._1, a._2 :: b._2, a._3 :: b._3)) 
+1

aber es für möglich Leser recht kryptisch ist –

0

Ich weiß nicht über elegant, aber Sie könnte tun es in einer Zeile ohne den Zwischenschritt der Speicherung der Tupel. Vielleicht ist es ein wenig schwer zu lesen ...

(for(split <- listOfStrings.map(_.split(":"))) 
    yield List(split(0), split(1), split(2))).transpose 

repl Beispiel:

scala> listOfStrings 
res1: List[java.lang.String] = List(a:b:c, d:e:f, g:h:i) 

scala> (for(split <- listOfStrings.map(_.split(":"))) 
    | yield List(split(0), split(1), split(2))).transpose 
res2: List[List[java.lang.String]] = List(List(a, d, g), List(b, e, h), List(c, f, i)) 
3

Wenn Sie etwas, das für beliebige Tupel Größen verwendet werden können:

val tupleSize = 3 
0.until(tupleSize).toList 
    .map(x => (_:Product).productElement(x).asInstanceOf[String]) 
    .map(tuples.map(_)) 

Offensichtlich ist dies könnte eleganter ausgedrückt werden, wenn Sie stattdessen eine Liste von Arrays hätten.

+3

+1 für eine Lösung ohne _1, _2 und _3! –

+1

Oder einfach: 'tuples.map (_. ProductIterator.toList) .transpose.asInstanceOf [Liste [Liste [String]]'. Ich wollte diese Lösung posten, aber die Besetzung ist schade. –

+0

Ich dachte auch über diese Lösung nach, aber diese Version ist wahrscheinlich effizienter als transponieren. Die Besetzung kann vermieden werden, wenn wir eine 'Liste' von' Arrays' als Eingabe nehmen. –

10

Sie wollen unzip:

scala> val (numbers, homonyms) = List(("one", "won"), ("two", "too")).unzip 
numbers: List[java.lang.String] = List(one, two) 
homonyms: List[java.lang.String] = List(won, too) 
+2

Das funktioniert nur bei Paaren, aber nicht bei anderen Tupeln –

+1

Es gibt Unzip3 für Tripel. – Blaisorblade

Verwandte Themen