惯用地将地图合并到具有clojure的值集的地图中

时间:2012-06-27 11:52:05

标签: clojure idioms

我正在尝试使用clojure将多个地图合并到一个地图中。

输入

{:a 1 :b "a"}
{:a 2 :b "b"}
{:a 3 :b "c"}
{:a 4 :b "a"}

预期

{:a #{1,2,3,4}, :b #{"a" "b" "c"}} 

每个键的值都会转换为原始地图中的一组值。

7 个答案:

答案 0 :(得分:5)

我使用merge-with,使用包含空集的预构建结构:

(def data [{:a 1 :b "a"}
           {:a 2 :b "b"}
           {:a 3 :b "c"}
           {:a 4 :b "a"}])

(let [base {:a #{} :b #{}}]
  (apply merge-with conj base data))

=> {:a #{1 2 3 4}, :b #{"a" "b" "c"}}

在基本映射中使用空集的技巧是conj有一个具体的对象可以处理,因此可以正常工作。

答案 1 :(得分:4)

merge-with可用于此:

(def d [{:a 1 :b "a"}
        {:a 2 :b "b"}
        {:a 3 :b "c"}
        {:a 4 :b "a"}])

(def initial (into {} (map #(vector  %1 [])  (keys (apply merge d)))))

(into {} (map (fn [[a b]] [a (set b)]) 
           (apply merge-with (fn [a b] (conj a b)) initial  d)))

答案 2 :(得分:2)

(defn value-sets [& maps]
  (reduce (fn [acc map]
            (reduce (fn [m [k v]]
                      (update-in m [k] (fnil conj #{}) v))
                    acc
                    map))
          {} maps))

编辑或者

(defn value-sets [& maps]
  (reduce (fn [acc [k v]]
            (update-in acc [k] (fnil conj #{}) v))
          {}
          (apply concat maps)))

多年后进一步编辑:我现在写的不同了:

(defn value-sets [maps]
  (apply merge-with into (for [m maps, [k v] m] 
                           {k #{v}})))

答案 3 :(得分:1)

看到涉及reduce的amalloy解决方案向我提出了这个建议:

(def maps [{:a 1 :b 2}
           {:a 11 :b 22 :c 5}
           {:c 6 :a 7}])

(defn my-merge-maps
  [maps]
  (reduce (fn [accum [k v]]
            (if (accum k)
              (assoc accum k (conj (accum k) v))
              (assoc accum k #{v})))
          {}
          (apply concat maps)))

(defn -main
  []
  (println (my-merge-maps maps)))

结果是:{:c #{5 6}, :b #{2 22}, :a #{1 7 11}}

(感谢gfredericks指出我不小心使用了varargs。:))

编辑:以下是另一种方式,使用merge-with

(def maps [{:a 1 :b 2}
           {:a 11 :b 22 :c 5}
           {:c 6 :a 7}])

(defn build-up-set
  [curr-val new-val]
  (if (set? curr-val)
    (conj curr-val new-val)
    #{curr-val new-val}))

(defn my-merge-maps
  [maps]
  (apply merge-with build-up-set maps))

(defn -main
  []
  (println (my-merge-maps maps)))

答案 4 :(得分:0)

这是一个流水线方法:

(defn my-merge-maps [stuff]
  (->> stuff
       (apply concat)
       (group-by first)
       (map (fn [[k vs]] [k (set (map second vs))]))
       (into {})))

如果我们有正确的核心功能来操作它们,可以在地图中完成吗?

答案 5 :(得分:0)

(def data [{:a 1 :b "a"}
           {:a 2 :b "b"}
           {:a 3 :b "c"}
           {:a 4 :b "a"}])

(apply
  merge-with
  clojure.set/union
  (map #(zipmap (keys %) (map hash-set (vals %))) data))
;{:b #{"a" "b" "c"}, :a #{1 2 3 4}}

答案 6 :(得分:0)

Amalloy昨天就IRC上的同一问题放弃了以下解决方案:

(defn value-sets [& maps]                                                      
  (apply merge-with into (for [m maps, [k v] m] {k #{v}})))