Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the idiomatic way to assoc several keys/values in a nested map in Clojure?

Tags:

idioms

clojure

Imagine you have a map like this:

(def person {   :name {     :first-name "John"     :middle-name "Michael"     :last-name "Smith" }}) 

What is the idiomatic way to change values associated with both :first-name and :last-name in one expression?

(Clarification: Let's say you want to set :first-name to "Bob" and :last-name to "Doe". Let's also say that this map has some other values in it that we want to preserve, so constructing it from scratch is not an option)

like image 656
byteclub Avatar asked Dec 21 '10 00:12

byteclub


1 Answers

Here are a couple of ways.

user> (update-in person [:name] assoc :first-name "Bob" :last-name "Doe") {:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}  user> (update-in person [:name] merge {:first-name "Bob" :last-name "Doe"}) {:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}  user> (update-in person [:name] into {:first-name "Bob" :last-name "Doe"}) {:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}}  user> (-> person            (assoc-in [:name :first-name] "Bob")           (assoc-in [:name :last-name]  "Doe")) {:name {:middle-name "Michael", :last-name "Doe", :first-name "Bob"}} 

Edit

update-in does recursive assocs on your map. In this case it's roughly equivalent to:

user> (assoc person :name               (assoc (:name person)                      :first-name "Bob"                      :last-name "Doe")) 

The repetition of keys becomes more and more tedious as you go deeper into a series of nested maps. update-in's recursion lets you avoid repeating keys (e.g. :name) over and over; intermediary results are stored on the stack between recursive calls. Take a look at the source for update-in to see how it's done.

user> (def foo {:bar {:baz {:quux 123}}}) #'user/foo  user> (assoc foo :bar               (assoc (:bar foo) :baz                      (assoc (:baz (:bar foo)) :quux                             (inc (:quux (:baz (:bar foo))))))) {:bar {:baz {:quux 124}}}  user> (update-in foo [:bar :baz :quux] inc) {:bar {:baz {:quux 124}}} 

assoc is dynamic (as are update-in, assoc-in, and most other Clojure functions that operate on Clojure data structures). If assoc onto a map, it returns a map. If you assoc onto a vector, it returns a vector. Look at the source for assoc and take a look in in RT.java in the Clojure source for details.

like image 172
Brian Carper Avatar answered Oct 07 '22 13:10

Brian Carper