(reduce-kv f init coll)
Reduces an associative collection. f should be a function of 3 arguments. Returns the result of applying f to init, the first key and the first value in coll, then applying f to that result and the 2nd key and value, etc. If coll contains no entries, returns init and f is not called. Note that reduce-kv is supported on vectors, where the keys will be the ordinals.
Let's assume you want to apply a function to a vector of maps,
Input: [{:a 1 :b 2} {:a 3 :b 4}]
such that all vals are incremented by 1.
Result: [{:a 2 :b 3} {:a 4 :b 5}]
An easy way to do so is using reduce-kv,
(def vector-of-maps [{:a 1 :b 2} {:a 3 :b 4}])
(defn update-map [m f]
(reduce-kv (fn [m k v]
(assoc m k (f v))) {} m))
(map #(update-map % inc) vector-of-maps)
=> ({:b 3, :a 2} {:b 5, :a 4})
;; Swap keys and values in a map
user=> (reduce-kv #(assoc %1 %3 %2) {} {:a 1 :b 2 :c 3})
{1 :a, 2 :b, 3 :c}
;; Swap keys with values, only if values are not empty,
;; while turning values into proper keys
(def someMap { :foo "food", :bar "barista", :baz "bazaar"})
(defn swap [someMap]
(reduce-kv (fn [m k v]
(if (empty? v) m (assoc m (keyword v) (name k)))) {} someMap))
(swap someMap)
=> {:food "foo", :barista "bar", :bazaar "baz"}
;; Calculate total wins and winning streaks
(def all-games
[{:game 1 :won true}
{:game 2 :won false}
{:game 3 :won true}
{:game 4 :won true}])
(reduce-kv
(fn [result index game]
(let [last-game (last result)
wins (if (:won game)
(inc (:total last-game 0))
(:total last-game))
streak (if (:won game)
(inc (:streak last-game 0))
0)]
(println (assoc game :total wins :streak streak))
(conj result (assoc game :total wins :streak streak))))
[]
all-games)
;; Output
;; {:game 1, :won true, :total 1, :streak 1}
;; {:game 2, :won false, :total 1, :streak 0}
;; {:game 3, :won true, :total 2, :streak 1}
;; {:game 4, :won true, :total 3, :streak 2}
;; [{:game 1, :won true, :total 1, :streak 1} {:game 2, :won false, :total 1, :streak 0} {:game 3, :won true, :total 2, :streak 1} {:game 4, :won true, :total 3, :streak 2}]
;; You can define map-kv using reduce-kv,
;; to do something to every value in a map.
(defn map-kv [f coll]
(reduce-kv (fn [m k v] (assoc m k (f v))) (empty coll) coll))
(map-kv inc {:a 12, :b 19, :c 2})
;;=> {:c 3, :b 20, :a 13}
;; It works on vectors, too.
(map-kv inc [1 1 2 3 5])
;;=> [2 2 3 4 6]
;; It works with indexes on vectors as well
(reduce-kv (fn [res idx itm] (assoc res idx itm)) {} ["one" "two" "three"])
;;=> {2 "three", 1 "two", 0 "one"}
(defn update-map-entries[m e]
(reduce-kv (fn [r k v] (assoc r k v)) m e))
;;user=> (update-map-entries {:a 1 :b 2 :c 3} {:a 5 :b 9})
;;{:a 5, :b 9, :c 3}
;;user=> (update-map-entries {:a 1 :b 2 :c 3} {:a 5 :b 9 :d 8})
;;{:a 5, :b 9, :c 3, :d 8}