Liking cljdoc? Tell your friends :D

Quick summary
Setup
API
Changelog
Usage
Performance
Alternatives
Contact

Smoosh

Exploring ways to combine exactly two Clojure hashmaps

Quick summary

This library provides a trio of functions for merging two hashmaps with 10–50% shorter evaluation times compared to Clojure's core counterparts.

Setup

Leiningen/Boot

[com.sagevisuals/smoosh "0"]

Clojure CLI/deps.edn

com.sagevisuals/smoosh {:mvn/version "0"}

Require

(require '[smoosh.core :refer [smerge smerge-with merge-merge]])

Zero dependencies

Copy-paste the code (license terms still apply).

Motivation & usage

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.

Speedier 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.

Speedier 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.

Speedier 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.

Alternatives

  • 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.


License

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

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close