The unexpected selection and merging behavior of clojure.set/join -


i wanted overwrite default value in relation specific values , stumbled upon behavior don't quite understand.

(clojure.set/join  #{   {:a 1 :b nil :c "2"}   {:a 2 :b "2" :c nil}   {:a 3 :b 1 :c 5} }  #{   {:a 1 :b 44}   {:a 3 :b 11 :c 55} }  {:a :a}) 

resolves #{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}}.

if flip arguments same result.

(clojure.set/join  #{   {:a 1 :b 44}   {:a 3 :b 11 :c 55} }  #{   {:a 1 :b nil :c "2"}   {:a 2 :b "2" :c nil}   {:a 3 :b 1 :c 5} }  {:a :a})  

also resolves #{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}}.

what expected (at least first statement) #{{:a 3, :c 55, :b 11} {:c "2", :a 1, :b 44}}.

first question: why don't expect?

second question: why same result regardless of argument order?


just in case wants solution problem stated in beginning

(   (fn [default, data, key]      (->> default        (map (fn [defaultentry]          (merge            defaultentry            (->> data (filter (fn [dataentry] (= (key defaultentry) (key dataentry)))) first)         )       ))     )   )   #{     {:a 1 :b nil :c "2"}     {:a 2 :b "2" :c nil}     {:a 3 :b 1 :c 5}   }    #{     {:a 1 :b 44}     {:a 3 :b 11 :c 55}   }    :a ) 

(warning: assumes key unique!)

from clojure.set/join code can see set fewer elements used index , set more elements reduced looking each element in index , keeping merged version each found item.

(defn join   "when passed 2 rels, returns rel corresponding natural   join. when passed additional keymap, joins on corresponding   keys."   ; 2-arity version removed brevity   ([xrel yrel km] ;arbitrary key mapping    (let [[r s k] (if (<= (count xrel) (count yrel))                    [xrel yrel (map-invert km)]                    [yrel xrel km])          idx (index r (vals k))]      (reduce (fn [ret x]                (let [found (idx (rename-keys (select-keys x (keys k)) k))]                  (if found                    (reduce #(conj %1 (merge %2 x)) ret found)                    ret)))              #{} s)))) 

that's why you'll same result (set/join b) or (set/join b a), except if both sets of same length, not case.

it explains why #{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}} instead of #{{:a 3, :c 55, :b 11} {:c "2", :a 1, :b 44}}: values in maps longer set take precedence on values shorter set when there's match.


Comments