Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating and accumulating a Map of Map of Map ... in scala

Tags:

scala

perl

I have a file containing data on Salesman, Product, Location, SalesValue

For instance:

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

The SalesValue can be succinctly accumulated into a hash of hash of hash in perl using the following code

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

Has anyone got any suggestions how this creation of a map of map of map, plus the accumulation , could best be implemented in scala?

like image 404
BarneyW Avatar asked Jan 15 '16 07:01

BarneyW


1 Answers

I would suggest to see the merging of these maps as a monoid-append operation.

First we create the maps of maps of maps as single elements:

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 is of type List[Map[String, Map[String, Map[String, Int]]]] :

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

Then we could suml using a library like scalaz or cats:

import scalaz._, Scalaz._

println(mmm.suml)

This will print (not idented):

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

To help understand what is happening behind the .suml operation I would shamelessly suggest to checkout this presentation I made last year https://speakerdeck.com/filippovitale/will-it-blend-scalasyd-february-2015


EDIT

We can also see our maps of maps of maps as Foldable and use foldMap for the same result:

input.map(_.split(", "))
     .foldMap{ case Array(n, g, c, v) => Map(n -> Map(g -> Map(c -> v.toInt))) }
like image 71
Filippo Vitale Avatar answered Sep 30 '22 12:09

Filippo Vitale