clojure.set/join的意外选择和合并行为 [英] The unexpected selection and merging behavior of clojure.set/join
问题描述
我想用某些特定值覆盖关系中的某些默认值,并偶然发现了一些我不太了解的行为.
I wanted to overwrite some default value in a relation with some specific values and stumbled upon some behavior I 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})
解析为#{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}}
.
如果我翻转参数,我将得到相同的结果.
If I flip the arguments I get the 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})
也解析为#{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}}
.
我期望(至少从第一句话开始)是#{{:a 3, :c 55, :b 11} {:c "2", :a 1, :b 44}}
.
What I expected (at least from the first statement) was #{{:a 3, :c 55, :b 11} {:c "2", :a 1, :b 44}}
.
第一个问题:为什么我没有得到我所期望的?
First question: Why don't I get what I expect?
第二个问题:为什么无论参数顺序如何都得到相同的结果?
Second question: Why do I get the same result regardless of argument order?
以防万一有人要我解决开头提到的问题
Just in case someone wants my solution to my problem stated in the 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
)
(警告:假定key
是唯一的!)
(Warning: Assumes that key
is unique!)
推荐答案
From the clojure.set/join code we can see that the set with fewer elements is used as an index and the set with more elements is reduced by looking for each element in the index and keeping the merged version for each found item.
(defn join
"When passed 2 rels, returns the rel corresponding to the natural
join. When passed an additional keymap, joins on the corresponding
keys."
; 2-arity version removed for 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))))
这就是为什么(set/join a b)
或(set/join b a)
会得到相同结果的原因,除非两组长度相同(不是您的情况).
That's why you'll the get same result with (set/join a b)
or (set/join b a)
, except if both sets are of the same length, which is not your case.
这也解释了为什么得到#{{:a 3, :c 5, :b 1} {:c "2", :a 1, :b nil}}
而不是#{{:a 3, :c 55, :b 11} {:c "2", :a 1, :b 44}}
的原因:存在匹配项时,较长集的映射中的值优先于较短集的映射中的值.
It also explains why you get #{{: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}}
: The values in the maps from the longer set take precedence over the values from the shorter set when there's a match.
这篇关于clojure.set/join的意外选择和合并行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!