2016-06-09 9 views
10

Qualcuno conosce un buon metodo per attivare la seguente lista input nell'elenco di output desiderato di seguito?Non so come eseguire questa trasformazione Elenco in Scala in modo ottimale

La funzione Sto cercando di creare

def transformList(input:List[(String,String)]):List[(String,String)] = ??? 

ingresso

val inputList = List(
    ("class","testClass1"), 
    ("class","testClass2"), 
    ("id","testId1"), 
    ("class","testClassRepeat"), 
    ("class","testClassRepeat"), 
    ("id","testId2"), 
    ("href","testHref1") 
) 

desiderata uscita

List(
    ("class","testClass1 testClass2 testClassRepeat testClassRepeat"), 
    ("id","testId1 testId2"), 
    ("href","testHref1") 
) 

ho una soluzione, ma non credo che lo sto facendo in un modo buono/efficiente. La soluzione che ho attualmente sto usando è:

  1. creare una mappa mutevole vuoto
  2. loop attraverso l'inputList con un .foreach
  3. Spingendo valori chiave/basati sul inputList nella mappa mutevole. Poi aggiungendo ai valori di chiavi esistenti, se del caso (per esempio, ci sono 4 "classi" nel mio esempio inputList.)

Grazie, Phil

+0

Usa 'sortWith',' 'groupBy' e mkString' - nessuna mutevolezza richiesto. –

risposta

4

È possibile utilizzare groupBy e può essere fatto in una sola riga .

scala> inputList.groupBy(_._1). 
      map{ case (key, value) => (key, value.map(_._2).mkString(" "))}.toList 

    res0: List[(String, String)] = List(
             (href,testHref1), 
             (class,testClass1 testClass2 testClassRepeat testClassRepeat), 
             (id,testId1 testId2) 
) 
+0

Sto usando questa soluzione. Grazie. Speravo che esistesse una soluzione a una linea. –

9
def f(xs: List[(String, String)]): Map[String, List[String]] = 
    xs.foldRight(Map.empty[String, List[String]]){ 
      (elem: (String, String), acc: Map[String, List[String]]) => 
      val (key, value) = elem 
      acc.get(key) match { 
       case None  => acc + (key -> List(value)) 
       case Some(ys) => acc.updated(key, value :: ys) 
      } 
    } 

scala> f(inputList) 
res2: Map[String,List[String]] = Map(
     href -> List(testHref1), 
     id -> List(testId1, testId2), 
     class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat) 
    ) 
+0

Grazie, Kevin. –

2

È possibile utilizzare foldLeft sulla raccolta differenziata :

5

Forse groupBy() è quello che cerchi?

scala> inputList.groupBy(_._1) 
res0: Map[String,List[(String, String)]] = Map(
     href -> List((href,testHref1)), 
     class -> List((class,testClass1), (class,testClass2), (class,testClassRepeat), (class,testClassRepeat)), 
     id -> List((id,testId1), (id,testId2)) 
    ) 

È anche abbastanza semplice pulire l'elenco di tuple mentre ci siamo, ad es.

scala> inputList.groupBy(_._1).map(kv => (kv._1, kv._2.map(_._2))) 
res1: Map[String,List[String]] = Map(
     href -> List(testHref1), 
     class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat), 
     id -> List(testId1, testId2) 
    ) 
2

Un altro modo con una per la comprensione, ma utilizzando anche groupBy (vedi @hezamu):

val m = 
    for { (k, xs) <- inputList.groupBy(_._1) 
     s = xs.map(_._2).mkString(" ") 
     } 
    yield k -> s 

e poi

m.toMap 
2

È possibile utilizzare groupBy con for comprehension per rendere il codice più in linea con i concetti di SQL relazionali in cui il risultato è necessario è analogo a un Group By sulla chiave e poi trasformando il risultato raggruppato, in questo caso concatenare la stringa:

def transformList(input:List[(String,String)]):List[(String,String)] = { 
    (for { 
    // Return the generator for key-values of grouped result 
    (k, v) <- input.groupBy(y => y._1) 
    // For every list in the grouped result return the concatenated string 
    z = v.map(_._2).mkString(" ") 
    } yield k -> z) 
    .toList 

}

Problemi correlati