merge-with

added
1.0

ns
clojure.core

type
function

(merge-with f & maps)

Returns a map that consists of the rest of the maps conj-ed onto
the first.  If a key occurs in more than one map, the mapping(s)
from the latter (left-to-right) will be combined with the mapping in
the result by calling (f val-in-result val-in-latter).

                (merge-with into
\t  {"Lisp" ["Common Lisp" "Clojure"]
\t   "ML" ["Caml" "Objective Caml"]}
\t  {"Lisp" ["Scheme"]
\t   "ML" ["Standard ML"]})
;;=> {"Lisp" ["Common Lisp" "Clojure" "Scheme"], "ML" ["Caml" "Objective Caml" "Standard ML"]}
            
                ;; merge two maps using the addition function

(merge-with + 
            {:a 1  :b 2}
            {:a 9  :b 98 :c 0})   
;;=> {:c 0, :a 10, :b 100}
            
                ;; 'merge-with' works with an arbitrary number of maps:

(merge-with + 
           {:a 1  :b 2}
           {:a 9  :b 98  :c 0}
           {:a 10 :b 100 :c 10}
           {:a 5}
           {:c 5  :d 42})
    
;;=> {:d 42, :c 15, :a 25, :b 200}
            
                ;; Use union to merge sets of elements
(use 'clojure.set)
(merge-with union
            {:a #{1 2 3},   :b #{4 5 6}}
            {:a #{2 3 7 8}, :c #{1 2 3}})

;;=> {:c #{1 2 3}, :a #{1 2 3 7 8}, :b #{4 5 6}}
            
                ;; Demonstrating difference between merge and merge-with

;; For merge the value from the right-most map wins:
(merge {:a 1} {:a 2} {:a 3})
;;=> {:a 3}

;; while for merge-with values are merged (with function + in this example):
(merge-with + {:a 1} {:a 2} {:a 3})
;;=> {:a 6}
            
                ;; Use merge-with and merge to merge values that are one level deep maps.

(merge-with merge {:x {:y 1}} {:x {:z 2}})
;;=> {:x {:z 2, :y 1}}

;; Deeper maps are not merged:
(merge-with merge {:x {:y {:a 1}}} {:x {:y {:b 2}}})
;;=>{:x {:y {:b 2}}}
            
                ;;Use into to avoid losing the shape (i.e. a vector) of the original data:
(merge-with into
\t  {"Lisp" ["Common Lisp" "Clojure"]
\t   "ML" ["Caml" "Objective Caml"]}
\t  {"Lisp" ["Scheme"]
\t   "ML" ["Standard ML"]})
;;=> {"Lisp" ["Common Lisp" "Clojure" "Scheme"], "ML" ["Caml" "Objective Caml" "Standard ML"]}

;;No need to use type-specific verbs such as union:
(merge-with into
            {:a #{1 2 3},   :b #{4 5 6}}
            {:a #{2 3 7 8}, :c #{1 2 3}})
;;=> {:c #{1 2 3}, :a #{1 2 3 7 8}, :b #{4 5 6}}
            
                ;; Note that merge-with is fundamentally additive, which can have unintuitive
;; consequences if you are using a subtractive operation.
(require '[clojure.set :as set])

;; Subtract members of one set from another with the same keys:

(merge-with set/difference {:a #{1 2 3}} {:a #{1}})
;;=> {:a #{3 2}}

(merge-with set/difference {:a #{1 2 3} :b #{2}} {:a #{1} :b #{4}})
;;=> {:a #{3 2}, :b #{2}}

;; If a key in the second map doesn't occur in the first, (merge-with) will 
;; simply copy it as in (merge), and the passed-in function will not be called:

(merge-with set/difference {:a #{1 2 3}} {:a #{1} :z #{4}})
;;=> {:a #{3 2}, :z #{4}}

;; The solution in this case is to ensure that both maps have the same keys:

(merge-with set/difference {:a #{1 2 3} :z #{}} {:a #{1} :z #{4}})
;;=> {:a #{3 2}, :z #{}}