相当于scala中的assoc-in(clojure)

时间:2018-11-29 07:34:46

标签: scala dictionary merge clojure overwrite

我正在尝试在scala中找到assoc-in(clojure)的等效项。我正在尝试转换

(defn- organiseDataByTradeId [data]
(reduce #(let [a (assoc-in %1 
                        [(%2 "internaltradeid") (read-string (%2 "paramseqnum")) "levelcols"] 
                        (reduce (fn [m k](assoc m k (get %2 k))) 
                                {} 
                                (string/split xmlLevelAttributesStr #",")))
            b (assoc-in a



                      [(%2 "internaltradeid") (read-string (%2 "paramseqnum")) "subLevelCols" (read-string (%2 "cashflowseqnum"))]
                         (reduce (fn [m k] (assoc m k (get %2 k))) 
                                {} 
                                (string/split xmlSubLevelAttributesStr #","))               
                        )] 
            b)
      {}
      data))

进入scala。 尝试过这个:

def organiseDataByTradeId(data: List[Map[String, String]]) = {
    data.map { entry => Map(entry("internaltradeid") -> Map(entry("paramseqnum").toInt -> Map("levelcols" -> (xmlLevelAttributesStr.split(",")).map{key=> (key,entry(key))}.toMap,
        "subLevelCols" -> Map(entry("cashflowseqnum").asInstanceOf[String].toInt -> (xmlSubLevelAttributesStr.split(",")).map{key=> (key,entry(key))}.toMap)))) }

  }

不确定如何在不覆盖的情况下合并我得到的地图列表。 这里 data 列表[Map [String,String]] 基本上是描述一个表。每个条目都是一行。列名是映射的键,值是值。 strong> xmlLevelAttributeStr 和 xmlSubLevelAttributeStr 是两个字符串,其中列名之间用逗号分隔。 我对scala还是陌生的。我将每一行(Map [String,String])转换为一个Scala Map,现在不知道如何合并它们,这样以前的数据就不会被覆盖并且行为与clojure代码完全相同。此外,我也不允许使用外部库,例如斯卡拉兹。

1 个答案:

答案 0 :(得分:1)

此Clojure代码不是一个很好的复制模式:它有很多重复项,并且很少解释它在做什么。我会这样写:

(defn- organiseDataByTradeId [data]
  (let [level-reader (fn [attr-list]
                       (let [levels (string/split attr-list #",")]
                         (fn [item]
                           (into {} (for [level levels]
                                      [level (get item level)])))))
        attr-levels (level-reader xmlLevelAttributesStr)
        sub-levels (level-reader xmlSubLevelAttributesStr)]
    (reduce (fn [acc item]
              (update-in acc [(item "internaltradeid"),
                              (read-string (item "paramseqnum"))]
                         (fn [trade]
                           (-> trade
                               (assoc "levelcols" (attr-levels item))
                               (assoc-in ["subLevelCols", (read-string (item "cashflowseqnum"))]
                                         (sub-levels item))))))
            {}, data)))

比原始代码多了几行代码,但是我借此机会命名了许多有用的概念,并将重复内容提取到局部函数中,以使其更加不言自明。

如果您知道将不会重复使用内部贸易编号,那就更容易了:您可以简单地生成许多独立的图并将它们合并在一起:

(defn- organiseDataByTradeId [data]
  (let [level-reader (fn [attr-list]
                       (let [levels (string/split attr-list #",")]
                         (fn [item]
                           (into {} (for [level levels]
                                      [level (get item level)])))))
        attr-levels (level-reader xmlLevelAttributesStr)
        sub-levels (level-reader xmlSubLevelAttributesStr)]
    (apply merge (for [item data]
                   {(item "internaltradeid")
                    {(read-string (item "paramseqnum"))
                     {"levelcols" (attr-levels item),
                      "subLevelCols" {(read-string (item "cashflowseqnum")) (sub-levels item)}}}}))))

但是,实际上,这两种方法都不适合在Scala中使用,因为Scala与Clojure具有不同的数据建模原理。 Clojure鼓励像这样宽松定义的异构地图,Scala希望您的地图是同质的。当您需要将多种类型的数据混合在一起时,Scala建议您定义一个类(或者也许是一个案例类-我不是Scala专家),然后创建该类的实例。

因此,在这里您需要一个Map[String, Map[Int, TradeInfo]],其中TradeInfo是一个具有两个字段levelcols : List[Attribute]subLevelCols作为类对的类(或者可能是一个单元素映射),其中包含一个cashflowseqnum和另一个List[Attribute]

一旦您以Scala方式对数据建模,就不会使用任何看起来像assoc-in的东西了,因为您的数据将不会是一张巨大的地图,因此问题就不会出现了。出现。