如何使用merge-with在clojure中创建重复键的列表

时间:2013-04-07 02:51:16

标签: clojure

我第一次尝试

(merge-with list maps)

它适用于具有单一值的地图, 但对于像

这样的东西
{:a [1 2]} {:a [2 3]} {:a [3 4]}

它给了我

{:a (([1 2] [2 3]) [3 4])}

但我想要

{:a ([1 2] [2 3] [3 4])}

我刚刚发现我可以使用vector包装地图的val:

(defn my-merge
  [& coll]
  (let [maps (map #(assoc % (first (keys %)) [(first (vals %))]) coll)]
    (apply merge-with concat maps)))

但是let绑定似乎太嵌套了,有没有更简单的函数可以用来代替concat?所以我可以有一些代码,如:

(merge-with the-function maps)

3 个答案:

答案 0 :(得分:0)

你可以试试这个:

(merge-with (fn [a b] 
  (if (list? a) 
      (conj a b) 
      (list a b))) 
  maps)

;; {:a ([3 4] [1 2] [2 3])}

它可以工作,但依赖于输出集合的类型 - 在这种情况下列表。这是我提出的最短时间。

希望这有帮助。

答案 1 :(得分:0)

版本1

(defn my-merge [f & mlist] 
  (into {} 
        (map (fn [[k v]] [k (apply f (map val v))]) 
             (group-by key (apply concat mlist)))))

(my-merge list
          {:a [1 2]} 
          {:a [2 3] :b :two} 
          {:a [3 4] :b :one})

=> {:a ([1 2] [2 3] [3 4]) :b (:two :one)}

第2版

(defn my-merge [f initv & mlist] 
  (apply merge-with f 
         (apply merge-with (fn [_ _] initv) mlist)
         mlist))

(my-merge conj ()
          {:a [1 2]} 
          {:a [2 3] :b :two} 
          {:a [3 4] :b :one})

=> {:b (:one :two), :a ([3 4] [2 3] [1 2])}

答案 2 :(得分:0)

以下解决方案还确保列表中项目的顺序基于地图的顺序。

(def maps [{:a [1 2]} 
          {:a [2 3] :b :two} 
          {:a [3 4] :b :one}])

(into {} (for [k (into #{} (mapcat keys maps))
               :let [obj (Object.)]]
           [k (filter (partial not= obj)
                      (map #(get % k obj) maps))]))

=> {:a ([1 2] [2 3] [3 4]), :b (:two :one)}