Reputation: 58786
I'm trying to use Clojure's update-in function but I can't seem to understand why I need to pass in a function?
Upvotes: 17
Views: 9928
Reputation: 1057
I think the short answer is that the function passed to update-in lets you update values in a single step, rather than 3 (lookup, calculate new value, set).
Coincidentally, just today I ran across this use of update-in in a Clojure presentation by Howard Lewis Ship:
(def in-str "this is this")
(reduce
(fn [m k] (update-in m [k] #(inc (or % 0))))
{}
(seq in-str))
==> {\space 2, \s 3, \i 3, \h 2, \t 2}
Each call to update-in takes a letter as a key, looks it up in the map, and if it's found there increments the letter count (else sets it to 1). The reduce drives the process by starting with an empty map {} and repeatedly applies the update-in with successive characters from the input string. The result is a map of letter frequencies. Slick.
Note 1: clojure.core/frequencies is similar but uses assoc! rather than update-in.
Note 2: You can replace #(inc (or % 0)) with (fnil inc 0). From here: fnil
Upvotes: 4
Reputation: 1092
A practical example you see here.
Type this snippet (in your REPL):
(def my-map {:useless-key "key"})
;;{:useless-key "key"}
(def my-map (update-in my-map [:yourkey] #(cons 1 %)))
;;{:yourkey (1), :useless-key "key"}
Note that :yourkey
is new. So the value - of :yourkey
- passed to the lambda is null
. cons
will put 1
as the single element of your list. Now do the following:
(def my-map (update-in my-map [:yourkey] #(cons 25 %)))
;;{:yourkey (25 1), :useless-key "key"}
And that is it, in the second part, the anonymous function takes the list - the value for :yourkey - as argument and just cons 25 to it.
Since our my-map
is immutable, update-in
will always return a new version of your map letting you do something with the old value of the given key.
Hope it helped!
Upvotes: 2
Reputation: 15259
This isn't a direct answer to your question, but one reason why a function like update-in
could exist would be for efficiency—not just convenience—if it were able to update the value in the map "in-place". That is, rather than
one can instead imagine an algorithm that would omit the second search for the key:
Unfortunately, the current implementation of update-in
does not do this "in-place" update. It uses get
for the extraction and assoc
for the replacement. Unless assoc
is using some caching of the last looked up key and the corresponding key-value tuple, the call to assoc
winds up having to seek the key again.
Upvotes: 6
Reputation: 370162
update-in
takes a function, so you can update a value at a given position depending on the old value more concisely. For example instead of:
(assoc-in m [list of keys] (inc (get-in m [list of keys])))
you can write:
(update-in m [list of keys] inc)
Of course if the new value does not depend on the old value, assoc-in
is sufficient and you don't need to use update-in
.
Upvotes: 31