GBarroso
GBarroso

Reputation: 477

Right way to change a value on a map on clojure

Alright, I'm new to clojure, this should be easy but for the life of me I can't find the answer

Let's say I have this map

(def mymap {:a 10 :b 15})

Now I want to change the value of :a to 5. I don't know how to do this properly

I know update and assoc can make changes but they both receive a function as last argument, which applies to the value. I don't want that, I don't want any function to run, I just want to simply set :a to 5.

I think I can pass an anonymous function that simply returns 5 and ignores the arg, but is this the right way? Doesn't look good to me

(update mymap :a (fn [arg] 5))

Upvotes: 8

Views: 10486

Answers (2)

Carcigenicate
Carcigenicate

Reputation: 45741

assoc does not take a function as its last argument; unless you were wanting to associate a function with a key in the map. (assoc mymap :a 5) does what you want.

I'll add though, update, which does take a function, could be used here as well when combined with constantly or just another function (although there's no reason to use them over assoc):

; constantly returns a function that throws away any arguments given to it,
; and "constantly" returns the given value
(update mymap :a (constantly 5))

; Basically the same as above
(update mymap :a (fn [_] 5))

Upvotes: 11

Nick
Nick

Reputation: 1275

Do keep in mind that as mymap is immutable, so calling (update mymap :a (constantly 5)) or (assoc mymap :a 5) will return a map {:a 5 :b 15}, further references to mymap will continue to return the original value of {:a 10 :b 15}.

If you want to update the value for later calls, you can look at using atoms.

(defonce mymap (atom {:a 10 :b 15}))

(defn change-mymap [value]
  (swap! mymap #(assoc % :a value)))

Just make sure that when you want to reference the value of an atom, you dereference it with the @ symbol. For example: (clojure.pprint/pprint @mymap)

When you call (change-mymap 5) this will update the stored mymap value to set :a to a new value, leaving any other key-value pairs in your map alone. This can be helpful when you are mapping in updated state in client/server code when responding to inputs from the other system.

Also note that for nested maps, such as

(defonce nested (atom {:a "a value"
                       :b {:inner "Another value"
                           :count 3
                           :another {:value 5}}}))

You can address a particular value in your map by a path vector. You can use the get-in function to retrieve the value (get-in @nested [:b :another :value]) and you can use assoc-in or update-in with a path to update the values. This also allows you to extend a map. For example, with the above value of nested, you can add a whole section to the tree: (swap! nested #(assoc-in % [:a :b :c :d] "foo")) will update the initial map to look like this:

{:a {:b {:c {:d "foo"}}}
 :b {:inner "Another value"
     :count 3
     :another {:value 5}}}

Upvotes: 3

Related Questions