I need to very efficiently compare two maps in Clojure/Java, and return the difference as determined by Java’s .equals(..), with nil/null equivalent to “not present”.
i.e. I am looking for the most efficient way to a write a function like:
(map-difference
{:a 1, :b nil, :c 2, :d 3}
{:a 1, :b "Hidden", :c 3, :e 5})
=> {:b nil, :c 2, :d 3, :e nil}
I’d prefer an immutable Clojure map as output, but a Java map would also be fine if the performance improvement would be significant.
For what it’s worth, my basic test case / expectation of behaviour is that the following will be equal (up to the equivalence of null = “Not present”) for any two maps a and b:
a
(merge b (difference a b))
What would be the best way to implement this?
I’m not sure what the absolutely most efficient way to do this is, but here’s a couple of things which may be useful:
The basic expectation of behaviour from the question text is impossible: if
aandbare maps such thatbcontains at least one key not present ina,(merge b <sth>)cannot be equal toa.If you end up going with an interop solution but then need to go back to a
PersistentHashMapat some point, there’s alwaysIf you need to pass the keyset of a Clojure map to a Java method, you can use
If all keys involved are guaranteed to be
Comparable, this could be exploited for efficient computation ofdifferenceon maps with many keys (sort & merge scan). For unconstrained keys this is of course a no-go and for small maps it could actually hurt performance.It’s good to have a version written in Clojure, if only to set a baseline performance expectation. Here is one: (updated)
I think that just doing
(concat (keys m1) (keys m2))and possibly duplicating some work is likely more efficient most of the time than checking a given key is in “the other map” too at every step.To wrap up the answer, here’s a very simple-minded set-based version with the nice property that it says what it does — if I misunderstood the spec, it should be readily apparent here. 🙂