从 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
,尤其是当注册日期的数量可能可变或其中一些丢失时(例如,如果您有日期 1
和 3
对于第一条路径, 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 变体基本相似。
我有地图列表
( {: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
,尤其是当注册日期的数量可能可变或其中一些丢失时(例如,如果您有日期 1
和 3
对于第一条路径, 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 变体基本相似。