12

我无法找到以下问题的解决方案:

假设我有一张地图:

(def defaults {
  :name    "John"
  :surname "Doe"
  :info    {:date-of-birth "01-01-1980"
            :registered    [{:type "newsletter" :name "breaking news" }]}
})

然后我传递了一个类似的结构化映射,但我想合并向量并覆盖其余的键:

(def new {
  :name    "Peter"
  :info    {:date-of-birth "11-01-1986"
            :registered    [{:type "alert" :name "mobile-alert" }]}
})

我想要这个结果:

 {:name    "Peter"
  :surname "Doe"
  :info    {:date-of-birth "11-01-1986"
            :registered    [{:type "newsletter" :name "breaking news" }
                            {:type "alert"      :name "mobile-alert" }]}}

现在我可以使用静态语法轻松做到这一点,例如:

(reduce conj (get-in defaults [:info :registered]) (get-in new [:info :registered]))

(可能有更好的方法......)但我希望更多具有以下属性的动态函数:

  1. 在不知道结构的情况下保留两个映射中的所有键
  2. 使用正确映射中的值更新任何键
  3. 如果一个key的val是一个向量,那么conj这个向量和右边的map的向量(如果合适的key当然存在的话)

我在这里先向您的帮助表示感谢 :)

4

2 回答 2

25

您绝对应该查看合并功能。这是可能的实现:

(defn deep-merge [a b]
  (merge-with (fn [x y]
                (cond (map? y) (deep-merge x y) 
                      (vector? y) (concat x y) 
                      :else y)) 
                 a b))
于 2013-06-26T18:54:20.697 回答
0

这是这种功能的可能实现。这至少是一个起点,您可能需要一些额外的验证,具体取决于数据的可能结构(例如,如果覆盖地图的值是向量但默认地图中的值甚至不是集合怎么办?)。

(declare merge-maps)

(defn merge-map [x [k v]]
  (cond (vector? v)
          (assoc x k (vec (reduce conj (x k) v)))
        (map? v)
          (assoc x k (merge-maps (x k) v))
        :esle
          (assoc x k v)))

(defn merge-maps [x y]
  (reduce merge-map x y))

(merge-maps defaults new)

;= {:info {:date-of-birth "11-01-1986", 
;=         :registered [{:name "breaking news", :type "newsletter"} 
;=                      {:name "mobile-alert", :type "alert"}]}, 
;=  :name "Peter", 
;=  :surname "Doe"}
于 2013-06-26T18:41:58.233 回答