This library provides a trio of functions for merging two hashmaps with 10–50% shorter evaluation times compared to Clojure's core counterparts.
[com.sagevisuals/smoosh "0"]
com.sagevisuals/smoosh {:mvn/version "0"}
(require '[smoosh.core :refer [smerge smerge-with merge-merge]])
Copy-paste the code (license terms still apply).
Clojure's merge is implemented with primitive
reduce1 and conj. Is it was possible to wring out some speed improvements using more performant utilities, such as
speedier reduce, transients, etc.?
Yup, a trio of merge variants implemented with standard Clojure idioms based on reduce-kv, transients, etc., complete their performance benchmarks more speedily than Clojure's. Faster merges would greatly
speed up real-world tasks, such as multi-threaded reductions.
merge
Clojure's merge combines two hashmaps, with the later hashmap overwriting any shared keys. smerge is a speedier
variant composed
of everyday Clojure functions like reduce-kv, assoc, and transient/persistent!. Here's how
smerge works.
(smerge {:a 999, :b 2}
{:a 1, :c 3}) ;; => {:a 1, :b 2, :c 3}
The two hashmaps share a key, in this example, keyword :a. The later hashmap's value 2 wins. The remaining entries of
the two input hashmaps, associated to keywords :b and :c, respectively, propagate untouched to the output hashmap.
This example uses small hashmaps, i.e., less than one-hundred entries, so smerge's performance improvement won't be
noticeable. But when merging two larger hashmaps, say with thousands or millions of entries, smerge finishes in about half the time required by
merge.
merge-with
When using vanilla merge/smerge and the hashmaps share a key, the later hashmap wins. The
…‑with merging functions provide a different mechanism. We supply a function that adjudicates how the entries are combined.
smerge-with is a speedier version of Clojure's merge-with. Here's how it works.
(smerge-with + {:a 1, :c 2} {:b 2, :c 1}) ;; => {:a 1, :b 2, :c 3}
The two hashmaps share a key, :c. The values associated to that key are combined with the supplied merging function, in this
example, addition.
As with vanilla merge, since these are small hashmaps, we won't notice a performance difference compared to Clojure's
merge-with. However, if our hashmaps contain thousands or millions of entries, we'd see smerge-with complete
10–20% faster.
deep-merge
Clojure does not provide a recursive deep merge function, but there are variants floating around the community, including an implementation from the Contrib library. Smoosh's variant, merge‑merge, only
dives two levels deep, but is 35–50% faster. Let's look at some examples.
(merge-merge {:a {:b 1}} {:a {:c 2}}) ;; => {:a {:b 1, :c 2}}
The two hashmaps must contain top-level keys associated to merge-able child hashmaps. In this example, both hashmaps contain a key
:a, so the child hashmaps are merged. The child hashmaps are merged with vanilla later-hashmap-wins semantics. In this
example, the child hashmaps contain no keys in common, so the result is a union.
Maybe the second-level keys are also identical.
(merge-merge {:a {:b 1}} {:a {:b 2}}) ;; => {:a {:b 2}}
Now, the later hashmap wins.
What if the top-level keys are different?
(merge-merge {:a {:b 1}} {:c {:b 2}}) ;; => {:a {:b 1}, :c {:b 2}}
In this example, both child hashmaps propagate because the top level keys of the two hashmaps are different.
Ben Sless' clj-fast
Faster implementations of Clojure's core functions.
clj-commons' useful
A collection of generally-useful Clojure utility functions.
Clojure's core merge, merge-with, and the community's deep-merge
Tried and true. No deps.
James Reeves' medley
A lightweight Clojure/ClojureScript library of useful, mostly pure functions that are "missing" from clojure.core.
puppetlabs' clj-kitchensink
A library of utility functions that are common to … Clojure projects.
This program and the accompanying materials are made available under the terms of the MIT License.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |