我有一个clojure函数,它返回一系列1键映射。我想将这些地图合并到一个地图中;但是,如果有相同键的地图,我不想覆盖这些值,只是将它们组合成一个矢量。 merge
似乎会覆盖,merge-with
似乎严重扭曲了这种类型。
我有:
({:foo "hello"}
{:bar "world"}
{:baz "!!!"}
{:ball {:a "abc", :b "123"}}
{:ball {:a "def", :b "456"}}
{:ball {:a "ghi", :b "789"}})
我想:
{:foo "hello"
:bar "world"
:baz "!!!"
:ball [{:a "abc", :b "123"} {:a "def", :b "456"} {:a "ghi", :b "789"}]}
感谢。
答案 0 :(得分:2)
(def data ...) ;; your list of maps
(apply merge-with (comp flatten vector) data)
;; => {:baz "!!!", :ball ({:b "123", :a "abc"} {:b "456", :a "def"} {:b "789", :a "ghi"}), :bar "world", :foo "hello"}
注意: flatten
的使用适用于OP的情况,但不是在创建属于碰撞键的值的向量时合并地图的一般方法。
答案 1 :(得分:1)
"矢量安全"我可以提出的变体必须遍历所有键值对两次:
(->> (for [[k vs] (group-by key (apply concat data))]
(if (next vs)
[k (mapv val vs)]
(first vs)))
(into {}))
;; => {:foo "hello",
;; :bar "world",
;; :baz "!!!",
;; :ball [{:a "abc", :b "123"} ...]}
基本上,它按键对所有值进行分组,只有当它只包含一个元素时才会删除它们周围的seq。
完全线程版本(为了便于阅读):
(->> (apply concat data)
(group-by key)
(map
(fn [[k vs]]
(if (next vs)
[k (mapv val vs)]
(first vs))))
(into {}))
答案 2 :(得分:0)
为每个键设置一个可预测的类型,如果你想稍后再读它,可以省去头痛,但是如果你别无选择:带有自定义函数的merge-with
可以解决它:
(apply merge-with (fn [v1 v2] ((if (vector? v1) conj vector) v1 v2)) data)