从 clojure 中的另一个映射列表向映射列表中的映射添加键值对

Adding key- value pairs to maps in a list of maps from another list of maps in clojure

我有地图列表

 ( {:path "first" :size "1 gb"}  
   {:path "second" :size "500 mb"}
  ...)

和另一个地图列表

 ( {:path "first" :size "1 gb" :date "1"}
   {:path "second" :size "500 mb" :date "1"}
   {:path "first" :size "0.9 gb" :date "2"}...
   {:path "second" :size "400 mb" :date "2"}...
 ...)

我想将第一个地图列表转换成类似

的形式
( {:path "first" :sizeon1 "1 gb" :sizeon2 "0.9 gb"...}
  {:path "second" :sizeon1 "500 mb" :sizeon2 "400 mb"...}
  ....)

我是 Clojure 新手,很难做到这一点。 你能帮帮我吗?

当您将任务分解成更小的部分时,一切都会变得清晰。

首先,定义一个助手来在结果数据集中创建那些 :sizeon1 键:

(defn date-key
  [date]
  (keyword (str "sizeon" date)))

接下来,您想要将单路径数据的集合缩减为聚合地图,假设这样的集合看起来像您描述的那样:

[{:path "first" :size "1 gb" :date "1"}
 {:path "first" :size "0.9 gb" :date "2"}
 ;; ...
 ]

reduce 就是这样的工具:

(defn reduce-path
  [path-data]
  (reduce
    ;; A function that takes an accumulator map and an element in the collection
    ;; from which you take date and size and assoc them under the appropriate keys
    (fn [acc el]
      (let [{:keys [date size]} el]
        (assoc acc (date-key date) size)))
    ;; A starting value for the accumulator containing the common path
    ;; for this collection
    {:path (:path (first path-data))}
    ;; The collection of single path data to reduce
    path-data))

最后,获取包含不同路径的原始数据集,按路径对其进行分区,并将reduce-path函数映射到它上面。

(def data
  [{:path "first" :size "1 gb" :date "1"}
   {:path "first" :size "0.9 gb" :date "2"}
   {:path "second" :size "500 mb" :date "1"}
   {:path "second" :size "400 mb" :date "2"}])

(->> data
     (partition-by :path)
     (map reduce-path))

请注意,此代码假定您的初始 data 集合已按 :path 排序。否则,partition-by 将不会像您预期的那样工作,并且必须相应地准备数据。

(def data '({:path "first" :size "1 gb" :date "1"}
            {:path "second" :size "500 mb" :date "1"}
            {:path "first" :size "0.9 gb" :date "2"}
            {:path "second" :size "400 mb" :date "2"}))

(defn- reduce-group [g]
  (reduce (fn [acc m] (assoc acc
                             (keyword (str "sizeon" (:date m)))
                             (:size m)))
          (first g) g))

(let [groups (group-by :path data)]
  (map reduce-group (vals groups)))

我会做什么,是重新考虑生成的数据结构: 我不知道您可能会如何使用生成的集合,但命名键 :sizeonX,尤其是当注册日期的数量可能可变或其中一些丢失时(例如,如果您有日期 13 对于第一条路径, 1 2 3 5 对于第二条路径)导致一堆不可预测的命名键地图,这将使检索这些密钥变得更加困难。 在我看来,使用这种结构会更好:

{:path "first" :sizes {"1" "500" "2" "1g" "10" "222"}}

所以这个尺寸图很容易迭代和处理。

这就是我该怎么做:

(def data '({:path "first" :size "1 gb" :date "1"}
            {:path "first" :size "0.9 gb" :date "3"}
            {:path "second" :size "500 mb" :date "1"}
            {:path "second" :size "700 mb" :date "2"}
            {:path "second" :size "400 mb" :date "3"}
            {:path "second" :size "900 mb" :date "5"}))

(map (fn [[k v]] {:path k
                  :sizes (into {} (map (juxt :date :size) v))})
     (group-by :path data))

;; ({:path "first", :sizes {"1" "1 gb", "3" "0.9 gb"}} 
;;  {:path "second", :sizes {"1" "500 mb", 
;;                           "2" "700 mb", 
;;                           "3" "400 mb", 
;;                           "5" "900 mb"}})

更新

但由于您仍然需要问题的结构,我会这样做:

(map (fn [[k v]]
       (into {:path k}
             (map #(vector (keyword (str "sizeon" (:date %)))
                           (:size %))
              v)))
     (group-by :path data))

;;({:path "first", :sizeon1 "1 gb", :sizeon3 "0.9 gb"} 
;; {:path "second", 
;;  :sizeon1 "500 mb", :sizeon2 "700 mb", 
;;  :sizeon3 "400 mb", :sizeon5 "900 mb"})

这与@superkonduktr 变体基本相似。