Clojure 在不覆盖关键点的情况下合并贴图

Clojure 在不覆盖关键点的情况下合并贴图,clojure,Clojure,我有一个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"}})

我有一个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"}]}
谢谢

(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"}
注意:在OP的情况下使用
展平
是有效的,但在创建属于碰撞键的值向量时,这不是一种合并贴图的通用方法。

我可以想到的“向量安全”变体必须在所有键值对上迭代两次:

(->> (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 merge-with (fn [v1 v2] ((if (vector? v1) conj vector) v1 v2)) data)

这是可能的,但我会退后一步问为什么?如果地图上的值可以是标量,也可以是向量,这取决于是否存在重复项,那么地图有什么用处呢?具有一致值类型的映射将更易于使用。它的类型不是我能控制的。在同一个映射中有一个或多个值。最好不要合并的值是向量。是的。。。展平几乎可以保证不做你想做的事情:/@arrdem如果OP不知道返回的数据是什么,那么是的,展平不是一种方法。你们中的任何一位可以添加一个更适合一般情况的答案吗?不够聪明,看不出这是否是一个解决方案,但我从
几乎展平
展平-1
中得到了很多用处,这里引用:。
(apply merge-with (fn [v1 v2] ((if (vector? v1) conj vector) v1 v2)) data)