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))))})) |