开发者

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

Imagine you have a map like this:

(def person {
  :name {
    :first-name "John"
    :mi开发者_C百科ddle-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)


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.


I'm not sure if my case is quite the same but I had list of changes to apply:

(def updates [[[:name :first-name] "Bob"] 
              [[:name :last-name] "Doe"]])

In that case you can reduce over that list with assoc-in like this:

(reduce #(assoc-in %1 (first %2) (second %2)) person updates)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜