David Williams
David Williams

Reputation: 8654

How to Increment Values in a Map

I am wrapping my head around state in Clojure. I come from languages where state can be mutated. For example, in Python, I can create a dictionary, put some string => integer pairs inside, and then walk over the dictionary and increment the values.

How would I do this in idiomatic Clojure?

Upvotes: 10

Views: 4134

Answers (5)

Kannan Ramamoorthy
Kannan Ramamoorthy

Reputation: 4190

To slightly improve @Michiel Brokent's answer. This will work if the key already doesn't present.

(update my-map :a #(if (nil? %) 1 (inc %)))

Upvotes: 1

Michiel Borkent
Michiel Borkent

Reputation: 34850

(def my-map {:a 1 :b 2})
(zipmap (keys my-map) (map inc (vals my-map)))
;;=> {:b 3, :a 2}

To update only one value by key:

(update-in my-map [:b] inc) ;;=> {:a 1, :b 3}

Since Clojure 1.7 it's also possible to use update:

(update my-map :b inc)

Upvotes: 12

slipset
slipset

Reputation: 3078

I've been toying with the same idea, so I came up with:

(defn remap 
  "returns a function which takes a map as argument
   and applies f to each value in the map"
  [f]
  #(into {} (map (fn [[k v]] [k (f v)]) %)))

((remap inc) {:foo 1})
;=> {:foo 2}

or

(def inc-vals (remap inc))

(inc-vals {:foo 1})
;=> {:foo 2}

Upvotes: 0

om-nom-nom
om-nom-nom

Reputation: 62835

Just produce a new map and use it:

(def m {:a 3 :b 4})

(apply merge 
  (map (fn [[k v]] {k (inc v) }) m))

; {:b 5, :a 4}

Upvotes: 3

NielsK
NielsK

Reputation: 6956

To update multiple values, you could also take advantage of reduce taking an already filled accumulator, and applying a function on that and every member of the following collection.

=> (reduce (fn [a k] (update-in a k inc)) {:a 1 :b 2 :c 3 :d 4} [[:a] [:c]])
{:a 2, :c 4, :b 2, :d 4}

Be aware of the keys needing to be enclosed in vectors, but you can still do multiple update-ins in nested structures like the original update in.

If you made it a generalized function, you could automatically wrap a vector over a key by testing it with coll?:

(defn multi-update-in
  [m v f & args]
       (reduce
         (fn [acc p] (apply
                       (partial update-in acc (if (coll? p) p (vector p)) f)
                       args)) m v))

which would allow for single-level/key updates without the need for wrapping the keys in vectors

=> (multi-update-in {:a 1 :b 2 :c 3 :d 4} [:a :c] inc)
{:a 2, :c 4, :b 2, :d 4}

but still be able to do nested updates

(def people
  {"keith" {:age 27 :hobby "needlefelting"}
   "penelope" {:age 39 :hobby "thaiboxing"}
   "brian" {:age 12 :hobby "rocket science"}})

=> (multi-update-in people [["keith" :age] ["brian" :age]] inc)
   {"keith" {:age 28, :hobby "needlefelting"},
    "penelope" {:age 39, :hobby "thaiboxing"},
    "brian" {:age 13, :hobby "rocket science"}}

Upvotes: 2

Related Questions