stuwest
stuwest

Reputation: 928

How can I merge within a nested hashmap?

I have a nested hashmap with a structure something like the following:

(def pathmap {:order {:genus {:species {:cat2 "data", :cat1 "data"}}}})

What I need is a function like assoc-in that will allow me to add new key-value pairs to the innermost nested map rather than simply replacing what's already there. For example:

(my-assoc-merge pathmap [:order :genus :species] {:cat3 "data"})
;=> {:order {:genus {:species {:cat3 "data" :cat2 "data", :cat1 "data"}}}}

I thought I might be able to do this easily enough by altering the assoc-in function slightly to use merge-with and union:

(defn my-assoc-merge
  [m [k & ks] v]
  (if ks
    (assoc m k (my-assoc-merge (get m k) ks v))
    (assoc m k (merge-with clojure.set/union (get m k '{}) v))))

Unfortunately this gives me the error "CompilerException java.lang.UnsupportedOperationException: count not supported on this type: Keyword, compiling:(core.clj:34:12)". Where am I going wrong?

Upvotes: 5

Views: 1624

Answers (1)

A. Webb
A. Webb

Reputation: 26446

Actually, assoc-in already creates a new key if it does not exist. Usage is as follows:

(def pathmap {:order {:genus {:species {:cat2 "data", :cat1 "data"}}}})

(assoc-in pathmap [:order :genus :species :cat3] "data")
;=> {:order {:genus {:species {:cat2 "data", :cat3 "data", :cat1 "data"}}}}

If you prefer to merge a new map, use update-in with merge

(update-in pathmap [:order :genus :species] merge {:cat3 "data"})
;=> {:order {:genus {:species {:cat2 "data", :cat3 "data", :cat1 "data"}}}}

The problem with your my-assoc-merge is the union which works on sets. Change merge-with to merge and drop the union.

Upvotes: 5

Related Questions