在scala中创建和累积Map的Map Map

时间:2016-01-15 07:46:09

标签: perl scala

我有一个包含Salesman,Product,Location,SalesValue

数据的文件

例如:

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

可以使用以下代码将SalesValue简洁地累积到perl中哈希的哈希值

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

有没有人有任何建议如何创建地图地图的地图,加上积累,最好能在scala中实现?

2 个答案:

答案 0 :(得分:6)

我建议将这些地图合并为monoid-append操作。

首先,我们将地图地图的地图创建为单个元素:

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的类型为List[Map[String, Map[String, Map[String, Int]]]]

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

然后我们可以suml使用scalazcats这样的库:

import scalaz._, Scalaz._

println(mmm.suml)

这将打印(未提交):

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

为了帮助了解.suml操作背后发生的情况,我会无耻地建议查看我去年制作的演示文稿https://speakerdeck.com/filippovitale/will-it-blend-scalasyd-february-2015

编辑

我们还可以将地图地图的地图显示为Foldable,并使用foldMap获得相同的结果:

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

答案 1 :(得分:0)

Filippo Vitale代码更简洁优雅

这是一个强力解决方案:

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 }
    }}

给出了这个结果:

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