(update-in m ks f & args)
'Updates' a value in a nested associative structure, where ks is a sequence of keys and f is a function that will take the old value and any supplied args and return the new value, and returns a new nested structure. If any levels do not exist, hash-maps will be created.
(def users [{:name "James" :age 26} {:name "John" :age 43}])
#'user/users
;; similar to assoc-in but does not simply replace the item.
;; the specified function is performed on the matching item.
;; here the age of the second (index 1) user is incremented.
(update-in users [1 :age] inc)
;;=> [{:name "James", :age 26} {:name "John", :age 44}]
(def p {:name "James" :age 26})
#'user/p
(update-in p [:age] inc)
;;=> {:name "James", :age 27}
;; remember, the value of p hasn't changed!
(update-in p [:age] + 10)
;;=> {:name "James", :age 36}
;; Here we see that the keyed object is
;; the first argument in the function call.
;; i.e. :age (- 26 10) => 16
(update-in p [:age] - 10)
;;=> {:name "James", :age 16}
(defn char-cnt [s]
"Counts occurence of each character in s"
(reduce
(fn [m k]
(update-in m [k] (fnil inc 0)))
{}
s))
;; Note use of fnil above
;; - returns 0 if nil is passed to inc (avoids null pointer exception)
(char-cnt "foo-bar")
;;=> {\\r 1, \\a 1, \\b 1, \\- 1, \\o 2, \\f 1}
;; f has args
;; The keyed value is placed as the first argument
;; :a (/ 3 4 5) => 3/20
(update-in {:a 3} [:a] / 4 5)
;;=> {:a 3/20}
;; be careful with that empty path sequence, it's seldom what you want
(update-in {} [] (constantly {:k :v}))
;;=> {nil {:k :v}}
;; In general, you find that for a non-empty path
;; (get-in (update-in m path (constantly v)) path)
;; is equal to v.
;; Surprisingly this does not hold true in case of an empty path.
;;You can use update-in in a nested map too, in order to update more than
;;one value:
(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})
(update-in m [:1] assoc :value 1 :active true)
;;=>{:1 {:value 1, :active true}, :2 {:value 0, :active false}}
;; We may dig into multiple levels with `update-in`:
(def player1 {:name "Player 1" :attribs {:str 10 :int 11 :wis 9}})
(update-in player1 [:attribs :str] inc)
;; {:name "Player 1", :attribs {:str 11, :int 11, :wis 9}}
(update-in player1 [:attribs :str] * 2)
;; {:name "Player 1", :attribs {:str 20, :int 11, :wis 9}}
;; We can see one level via `update`...
(update player1 :attribs #(update % :str inc))
;; {:name "Player 1", :attribs {:str 11, :int 11, :wis 9}}
user=> (update-in {:a {:b 3}} [:a :b] inc)
;;=> {:a {:b 4}}
;; can also use in []
user=> (update-in [1 2 [1 2 3]] [2 0] inc)
;;=> [1 2 [2 2 3]]
;; can mix associative types as well
user=> (update-in [1 {:a 2 :b 3 :c 4}] [1 :c] (fnil inc 5))
;; => [1 {:a 2, :b 3, :c 5}]
user=> (update-in [1 {:a 2 :b 3 :c 4}] [1 :d] (fnil inc 5))
;; => [1 {:a 2, :b 3, :c 4, :d 6}]
;; but of course vector indices must be appropriate types
user=> (update-in [1 {:a 2 :b 3 :c 4}] [:b :c] (fnil inc 5))
IllegalArgumentException Key must be integer clojure.lang.APersistentVector.assoc (APersistentVector.java:345)