(def data {"Bob" {"A" 3.5 "B" 4.5 "C" 2.0}
"Jane" {"A" 2.0 "B" 1.5 "D" 4.0}})
调用
(merge-with + (data "Bob") (data "Jane"))
产生
{"A" 5.5, "B" 6.0, "C" 2.0 "D" 4.0}
我只想创建一个合并的地图,但仅限于公共密钥。结果我正在寻找 是
{"A" 5.5, "B" 6.0}
在clojure中这样做的好方法是什么?
答案 0 :(得分:5)
这是一种相当简单的单程方法,它应该胜过迄今为止建议的多遍方法,而不是特别难以阅读:
(defn merge-matching [f a b]
(into {}
(for [[k v] a
:let [e (find b k)]
:when e]
[k (f v (val e))])))
答案 1 :(得分:4)
面向性能的解决方案使用瞬态reduce-kv
和大小检查迭代较小的地图:
(defn merge-common-with [f m1 m2]
(let [[a b] (if (< (count m1) (count m2))
[m1 m2]
[m2 m1])]
(persistent!
(reduce-kv (fn [out k v]
(if (contains? b k)
(assoc! out k (f (get a k) (get b k)))
out))
(transient {})
a))))
在REPL中,使用问题文本中的示例数据:
(merge-common-with + (data "Bob") (data "Jane"))
;= {"A" 5.5, "B" 6.0}
请注意,虽然我希望在许多情况下上述方法是最快的方法,但我绝对会根据您的实际用例使用典型数据进行基准测试。这是基于Criterium的基准,使用问题文本中的data
(merge-common-with
在此获胜):
(require '[criterium.core :as c])
(def a (data "Bob"))
(def b (data "Jane"))
;; Hendekagon's elegant approach amended to select-keys on both sides
(defn merge-common-with* [f a b]
(merge-with f
(select-keys a (keys b))
(select-keys b (keys a))))
;; benchmarks for three approaches follow, fastest to slowest
(c/bench (merge-common-with + a b))
Evaluation count : 74876640 in 60 samples of 1247944 calls.
Execution time mean : 783.233604 ns
Execution time std-deviation : 7.660391 ns
Execution time lower quantile : 771.514052 ns ( 2.5%)
Execution time upper quantile : 802.622953 ns (97.5%)
Overhead used : 1.266543 ns
Found 3 outliers in 60 samples (5.0000 %)
low-severe 3 (5.0000 %)
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
(c/bench (merge-matching + a b)) ; amalloy's approach
Evaluation count : 57320640 in 60 samples of 955344 calls.
Execution time mean : 1.047921 µs
Execution time std-deviation : 16.221173 ns
Execution time lower quantile : 1.025001 µs ( 2.5%)
Execution time upper quantile : 1.076081 µs (97.5%)
Overhead used : 1.266543 ns
(c/bench (merge-common-with* + a b))
WARNING: Final GC required 3.4556868188006065 % of runtime
Evaluation count : 33121200 in 60 samples of 552020 calls.
Execution time mean : 1.862483 µs
Execution time std-deviation : 26.008801 ns
Execution time lower quantile : 1.821841 µs ( 2.5%)
Execution time upper quantile : 1.914336 µs (97.5%)
Overhead used : 1.266543 ns
Found 1 outliers in 60 samples (1.6667 %)
low-severe 1 (1.6667 %)
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
答案 2 :(得分:1)
(merge-with merge-fn A (select-keys B (keys A)))
答案 3 :(得分:0)
user> (let [data {"Bob" {"A" 3.5 "B" 4.5 "C" 2.0}
"Jane" {"A" 2.0 "B" 1.5 "D" 4.0}}
common-keys (apply clojure.set/intersection
(map (comp set keys second) data))]
(apply merge-with + (map #(select-keys % common-keys) (vals data))))
{"B" 6.0, "A" 5.5}
我将它概括了一点,因此可以更加了解传入数据
答案 4 :(得分:0)
如果您始终只想合并两个对象,您也可以执行此类操作。
(into {}
(for [[kx vx] (data "Bob")
[ky vy] (data "Jane")
:when (= kx ky)]
{kx (+ vx vy)})))
如果要合并多个对象,可以将上面的代码定义为函数,并像这样使用reduce。
(defn merge-objects [obj1 obj2]
(into {} (for [[kx vx] obj1 [ky vy] obj2 :when (= kx ky)] {kx (+ vx vy)})))
(reduce merge-objects (map data ["Bob" "Jane"]))
由于您实际上在两个地图上进行迭代,因此我不确定这可能会产生任何性能影响。但如果您的地图很小,您可能不必担心它。
答案 5 :(得分:0)
(defn reduce-merge [& maps]
(when (some identity maps)
(reduce #(select-keys (or %2 %1) (keys %1)) maps)))
对我来说工作得很好,or
是在地图列表中吞下nils。不处理深度合并或提供碰撞功能(这将始终发生)。