lmotl3
lmotl3

Reputation: 647

How to replace multiple values within a nested map using a function in clojure?

I'm new to Clojure, and I have a nested map structured like this:

{:players                       
    {"p1" 
        {:id      "p1"
         :deck    []
         :hand    [{:name        "Imp"
                    :entity-type :card}]
         :minions [{:damage-taken                0
                    :attacks-performed-this-turn 0
                    :entity-type                 :minion
                    :name                        "Imp"
                    :id                          "m1"
                    :position                    0
                    :owner-id                    "p1"}]
         :hero    
                   {:name         "Jaina Proudmoore"
                    :id           "h1"
                    :entity-type  :hero
                    :mana         0
                    :damage-taken 0}}
etc

If I wanted to replace hero with an new map, one with all the same keys but different values, how could I do it? I've tried mapping the update function onto the keys of hero, but that hasn't worked.

Upvotes: 2

Views: 204

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

there are two common functions for this, assoc-in for when you want to change one value and update-in when you want to use a function to change the values based on the current value and or want to change several values:

user> (def players {:players                       
                    {"p1" 
                     {:id      "p1"
                      :deck    []
                      :hand    [{:name        "Imp"
                                 :entity-type :card}]
                      :minions [{:damage-taken                0
                                 :attacks-performed-this-turn 0
                                 :entity-type                 :minion
                                 :name                        "Imp"
                                 :id                          "m1"
                                 :position                    0
                                 :owner-id                    "p1"}]
                      :hero    
                      {:name         "Jaina Proudmoore"
                       :id           "h1"
                       :entity-type  :hero
                       :mana         0
                       :damage-taken 0}}}})
#'user/players

In this case update-in is a good match because it let's you do whatever you want to the nested collection. here's an example that assoc's some new values based on the previous ones, your could also add your function to map over the keys here as well.

user> (-> (update-in players [:players "p1" :hero] 
                     #(assoc %
                             :name (str "long lost twin of " (:name %))
                             :id           "h2"
                             :entity-type  :antihero
                             :mana         (inc (:mana %))
                             :damage-taken (dec (:damage-taken %))))
          clojure.pprint/pprint)
{:players
 {"p1"
  {:id "p1",
   :deck [],
   :hand [{:name "Imp", :entity-type :card}],
   :minions
   [{:damage-taken 0,
     :attacks-performed-this-turn 0,
     :entity-type :minion,
     :name "Imp",
     :id "m1",
     :position 0,
     :owner-id "p1"}],
   :hero
   {:name "long lost twin of Jaina Proudmoore",
    :id "h2",
    :entity-type :antihero,
    :mana 1,
    :damage-taken -1}}}} 
user> 

Upvotes: 2

Related Questions