2012-09-12 9 views
12

Supponiamo che io ho il seguente elenco di tuple:Scala: Come convertire gli elementi tuple di liste

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

Vorrei ottenere la scissione (0) in un elenco, split (1) in un altro elenco e presto. Un modo semplice questo potrebbe essere realizzato è scrivendo:

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

Esiste un modo più elegante (funzionale) di realizzare quanto sopra senza scrivere 3 separati prospetti?

risposta

11

Questo vi darà il risultato come una lista di lista:

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

Se si desidera memorizzarli in variabili locali, basta fare:

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

UPDATE: Come indicato da Blaisorblade, la libreria standard effettivamente ha un metodo incorporato per questo: unzip3, che è proprio come unzip ma per triple invece di coppie:

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

Inutile dire, si dovrebbe favorire questo metodo rispetto la mia soluzione arrotolato a mano sopra (ma per le tuple di arity> 3, questo sarebbe ancora valido).

+0

troppo lento :) stava per inserire: 'tuples.map {t => t._1 :: :: t._2 t._3 :: Nil} .transpose' bello. +1 –

+0

Pessima idea, dovresti usare decomprimere dall'altra risposta: http://stackoverflow.com/a/17281359/53974 – Blaisorblade

+0

@Balisorblade: non sono d'accordo. Questo sarebbe vero se fosse un 'Tuple2', ma è un' Tuple3'. 'unzip' gestisce solo le coppie di decompressione, e l'esempio di jeshan mostra una lista di' Tuple3', non 'Tuple2'. –

1

Si potrebbe semplicemente scrivere le istruzioni in una singola riga.

Come

(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

ma è abbastanza criptico per un eventuale lettore –

0

Non so elegante ma si potrebbe farlo in una riga senza il passaggio intermedio di memorizzare le tuple. Forse è un po 'difficile da leggere ... esempio

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

repl:

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

Se si desidera qualcosa che può essere utilizzato per le dimensioni tuple arbitrarie:

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

Ovviamente, questo potrebbe essere espresso in modo più elegante se avessi invece una lista di matrici.

+3

+1 per una soluzione senza _1, _2 e _3! –

+1

O semplicemente: 'tuples.map (_. ProductIterator.toList) .transpose.asInstanceOf [List [List [String]]'. Stavo per pubblicare questa soluzione, ma il cast è un peccato. –

+0

Ho pensato anche a questa soluzione, ma questa versione è probabilmente più efficiente della trasposizione. Il cast può essere evitato se prendiamo un 'List' di' Array's come input. –

10

Si desidera decomprimere:

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

Questo funziona solo su coppie però, non su qualsiasi altro tipo di tupla –

+1

C'è unzip3 per le triple. – Blaisorblade