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?
You can do it like you assumed. But your HashMap has to be templated: Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>(); Otherwise you have to do a cast to Map after you retrieve the second map from the first.
Assuming that both maps contain the same set of keys, and that you want to "combine" the values, the thing you would be looking for is a Pair class, see here for example. You simply iterate one of the maps; and retrieve values from both maps; and create a Pair; and push that in your result map.
Comparing Nested HashMaps We can compare them using the equals() method. The default implementation compares each value.
The Map. putAll() method provides a quick and simple solution to merge two maps. This method copies all key-value pairs from the second map to the first map. Since a HashMap object can not store duplicate keys, the Map.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With