关于函数式编程:合并映射列表,并将值组合到Clojure中的集合

Merge list of maps and combine values to sets in Clojure

我可以在这里把什么函数作为FOO放到最后产生真值?我使用了哈希集(仅对前两个值正确),conj和concat,但我知道我对其中的任何一个都无法正确处理单元素vs设置条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(defn mergeMatches [propertyMapList]
   "Take a list of maps and merges them combining values into a set"
    (reduce #(merge-with FOO %1 %2) {} propertyMapList))

(def in
    (list
        {:a 1}
        {:a 2}
        {:a 3}
        {:b 4}
        {:b 5}
        {:b 6} ))

(def out
    { :a #{ 1 2 3}
      :b #{ 4 5 6} })

; this should return true
(= (mergeMatches in) out)

处理此问题的最惯用的方法是什么?


此操作将执行:

1
2
(let [set #(if (set? %) % #{%})]
  #(clojure.set/union (set %) (set %2)))

为示例(Alex)更直接地重写:

1
2
3
4
5
6
(defn to-set [s]
    (if (set? s) s #{s}))
(defn set-union [s1 s2]
    (clojure.set/union (to-set s1) (to-set s2)))
(defn mergeMatches [propertyMapList]
    (reduce #(merge-with set-union %1 %2) {} propertyMapList))


我没有写这个,但是它是@amitrathore在Twitter上贡献的:

1
2
3
4
(defn kv [bag [k v]]
  (update-in bag [k] conj v))
(defn mergeMatches [propertyMapList]
  (reduce #(reduce kv %1 %2) {} propertyMapList))


我不会为此使用合并功能,

1
2
3
4
5
6
7
8
9
(defn fnil [f not-found]
  (fn [x y] (f (if (nil? x) not-found x) y)))
(defn conj-in [m map-entry]
  (update-in m [(key map-entry)] (fnil conj #{}) (val map-entry)))
(defn merge-matches [property-map-list]
  (reduce conj-in {} (apply concat property-map-list)))

user=> (merge-matches in)
{:b #{4 5 6}, :a #{1 2 3}}

fnil将很快成为核心的一部分,因此您可以忽略实现...但是它只是创建了另一个可以处理nil参数的函数的版本。在这种情况下,conj将用#{}代替nil。

因此对于提供的地图列表中的每个键/值,归约都与一个集合相结合。


@wmacgyver在Twitter上基于多地图提供的另一种解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
(defn add
 "Adds key-value pairs the multimap."
  ([mm k v]
     (assoc mm k (conj (get mm k #{}) v)))
  ([mm k v & kvs]
     (apply add (add mm k v) kvs)))
(defn mm-merge
 "Merges the multimaps, taking the union of values."
  [& mms]
  (apply (partial merge-with union) mms))  

(defn mergeMatches [property-map-list]
  (reduce mm-merge (map #(add {} (key (first %)) (val (first %))) property-map-list)))


这似乎有效:

1
2
3
4
(defn FOO [v1 v2]
      (if (set? v1)
          (apply hash-set v2 v1)
          (hash-set v1 v2)))

不是很漂亮,但是可以。

1
2
3
(defn mergeMatches [propertyMapList]
    (for [k (set (for [pp propertyMapList] (key (first pp))))]
         {k (set (remove nil? (for [pp propertyMapList] (k pp))))}))