2016-01-15 22 views
5

Ho un file contenente i dati su Commesso, prodotti, l'ubicazione, SalesValueCreazione e accumulando una Mappa di Mappa di Mappa ... in scala

Per esempio:

Bob, Carrots, United States, 200 
Bill, Potatoes, England, 100 
Bob, Oranges, England, 50 
Bob, Carrots, United States, 20 

Il SalesValue può essere succintamente accumulata in un hash di hash di hash in perl utilizzando il seguente codice

while(<>){ 
    @cols = split(/,/); 
    $vals {$cols[0]} {$cols[1]} {$cols[2]} += $cols[3]; 
} 

qualcuno ha ottenuto qualche suggerimento di come questa creazione di una mappa di carta di carta, più l'accumulo, potrebbe essere meglio i implementato in scala?

risposta

6

Suggerirei di vedere la fusione di queste mappe come un'operazione monoid-append.

Innanzitutto creiamo le mappe di mappe di mappe come elementi singoli:

val input = """Bob, Carrots, United States, 200 
       |Bill, Potatoes, England, 100 
       |Bob, Oranges, England, 50 
       |Bob, Carrots, United States, 20""".stripMargin.lines.toList 

val mmm = input.map(_.split(", ")) 
       .map { case Array(n, g, c, v) => Map(n -> Map(g -> Map(c -> v.toInt))) } 

mmm è di tipo List[Map[String, Map[String, Map[String, Int]]]]:

List[Map[String, 
        Map[String, 
           Map[String, Int]]]] 

allora potremmo suml utilizzando una libreria come scalaz o cats:

import scalaz._, Scalaz._ 

println(mmm.suml) 

Questo stamperà (non l'IDENT):

Map(Bill -> Map(Potatoes -> Map(England -> 100)), 
    Bob -> Map(Oranges -> Map(England -> 50), 
       Carrots -> Map(United States -> 220))) 

per aiutare a capire che cosa sta accadendo dietro l'operazione .suml avrei spudoratamente suggerire alla cassa questa presentazione che ho fatto l'anno scorso https://speakerdeck.com/filippovitale/will-it-blend-scalasyd-february-2015


EDIT

Possiamo anche vedere le nostre mappe di mappe di mappe come Foldable e utilizzare foldMap per lo stesso risultato:

codici
input.map(_.split(", ")) 
    .foldMap{ case Array(n, g, c, v) => Map(n -> Map(g -> Map(c -> v.toInt))) } 
+2

In altre parole, Perl è il vincitore qui;) – Ashalynd

+0

... sì, ma aspetto Perl6 prima di usarlo di nuovo :-P Dopo questo troll aggiornerò la risposta con qualche spiegazione più dettagliata e una risposta alternativa –

+0

Ottimo modo per leggere in una mappa della mappa oof Maps ... – BarneyW

0

Filippo Vitale sono più concisi ed eleganti

Si tratta di una soluzione di bruteforce:

val t = 
    """Bob, Carrots, United States, 200 
    |Bill, Potatoes, England, 100 
    |Bob, Oranges, England, 50 
    |Bob, Carrots, United States, 20""".stripMargin 

def commaSplit(s: String) = s.splitAt(s.indexOf(",")) 

def f(arg: Seq[String]) = 
    arg 
    .groupBy(commaSplit(_)._1) 
    .map{ case (key, values) => key -> values.map(commaSplit(_)._2.drop(2))} 

val res = 
    f(t.split("\n")) 
    .map{ case (key, values) => key -> f(values).map { case (k, v) => 
     k -> f(v).map { case (country, amount) => country -> amount.map(_.toInt).sum } 
    }} 

che dà questo risultato:

Map(Bob -> Map(Carrots -> Map(United States -> 220), 
       Oranges -> Map(England -> 50)), 
    Bill -> Map(Potatoes -> Map(England -> 100)))