With a map in Java you can write map.computeIfAbsent(key, f)
that checks whether the key exists in the map already, and if not calls f
on the key to generate a value, and enters that in the map. You also have map.putIfAbsent(key, value)
which only puts the value in the map if the key does not already exist in the map.
Ignoring the fact the Java methods also return the value, and instead wanting the new map returned, what would be the equivalent code in Clojure?
The best I've come up with so far is to roll my own with something like
(defn compute-if-absent [map key f]
(if (key map)
map
(assoc map key (f key))))
Is there an alternative to rolling my own?
Clojure maps are immutable so you will always return a new map with updated contents - thus your functions will be always thread safe. It also means that you can't have a global variable holding your map and mutate them in place.
Your implementation of compute-if-absent
is almost correct. It will fail for keys that are falsey, for example {false 1}
. You need to change your if
condition and use contains?
instead of key
:
(defn compute-if-absent [m k f]
(if (contains? m k)
m
(assoc m key (f k))))
If you do need to have the same behaviour as ConcurrentMap
you can just use them using Java interop in Clojure.
Even simpler solution would be to use merge
, for put-if-absent
:
(merge {key val} m)
Although the end result would be the same for compute-if-absent
, the following code would of course compute (f key)
regardless of the contents of m
:
(merge {key (f key)} m)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With