Reputation: 3787
I want to add an entry to a map but only if the map does not contain the key that I want to add. I.e. I want to do insert but not update. For his I created 2 functions:
(defn assoc-if [pred coll k v]
(if pred (assoc coll k v) coll))
(defn assoc-if-new [coll k v]
(assoc-if (not (contains? coll k)) coll k v))
My question is, do these two functions not exist already?
Also, I'm pretty new to Clojure, any tips on the implementation?
Upvotes: 8
Views: 2458
Reputation: 13473
(defn assoc-if [pred coll k v]
(if (pred coll k) (assoc coll k v) coll))
(defn assoc-if-new [coll k v]
(assoc-if (complement contains?) coll k v))
You made a couple of errors:
In
(defn assoc-if [pred coll k v]
(if pred (assoc coll k v) coll))
... pred
is not called. Being neither false
nor nil
, its function value will evaluate true. So the function will always return (assoc coll k v)
.
In
(defn assoc-if-new [coll k v]
(assoc-if (not (contains? coll k)) coll k v))
... the first argument to assoc-if
should be a predicate - a function returning a value used for its truth or falsehood. (not (contains? coll k))
will produce a boolean value, causing an error when assoc-if
tries to call it as a function.
assoc-if
concocts the call
internally.contains?
to a function that returns the logically inverted result.
The standard function complement
does this.Upvotes: 6
Reputation: 2104
With merge
the right hand map will always overwrite the left hand map so if you reverse your arguments and put them in a map you get the same behaviour:
(assoc-if-new {:a 1 :b 2} :b 3)
;=> {:a 1, :b 2}
(assoc-if-new {:a 1 :b 2} :c 3)
;=> {:c 3, :a 1, :b 2}
(merge {:b 3} {:a 1 :b 2})
;=> {:a 1, :b 2}
(merge {:c 3} {:a 1 :b 2})
;=> {:b 2, :a 1, :c 3}
In other words:
(defn assoc-if-new [coll k v] (merge {k v} coll))
Upvotes: 20