Reputation: 999
I'm looking for a function to achieve the following example result:
{"foo1" "baz"
"foo2.bar" "baz"
"foo2.bar2" "baz"
"foo3_bar" "baz"}
=>
{:foo1 "baz"
:foo2 {:bar "baz"
:bar2 "baz"}
:foo3 {:bar "baz"}}
As one can see, it's a bit different from a classic deep-merge
as the keys have to be keyword
ized first in a way that dot- and underscore postfixes are converted to hash maps (instead of the usual #[_\.]=> -
).
Upvotes: 0
Views: 828
Reputation: 999
With inspiration from @lgrapenthin I came up for this solution. It is on the upside short and concise and on the downside expensive (which is not to bad for my use case) and the overwriting strategy is determined by Clojure's hash map sorting (aka for user's undetermined):
(defn- deep-merge [& maps]
(if (every? map? maps)
(apply merge-with deep-merge maps)
(last maps)))
(defn- str-keys-to-map [[k v]]
(let [ks (map keyword (filter not-empty (string/split k #"[\._]")))]
(when-not (empty? ks) (assoc-in {} ks v))))
(defn deep-keywordize-keys [m]
(->> m (map str-keys-to-map) (apply deep-merge)))
Upvotes: 0
Reputation: 116
You could use a function like this one. Please note that it could be optimized to do tail recursion.
(defn deep-hashmap-merge
[ m ]
(let
[
tget (fn [r k d]
(let
[ t (get r k d)]
(if (associative? t) t d)))
get-keylist-value (fn [r [k & ks] kv]
(if (nil? ks)
(assoc r k kv)
(assoc r k (get-keylist-value (tget r k {}) ks kv))))
]
(reduce #(get-keylist-value %1 (map keyword (clojure.string/split (first %2) #"[_\.]")) ( second %2)) {} m)
)
)
And the output would then be :
user=> (deep-hashmap-merge
#_=> {"foo" "baz"
#_=> "foo.bar" "baz"
#_=> "foo.bar2" "baz"
#_=> "foo2_bar" "baz"})
{:foo {:bar "baz", :bar2 "baz"}, :foo2 {:bar "baz"}}
Upvotes: -1
Reputation: 9266
(defn parse-keys-and-merge
[hm]
(reduce-kv (fn [hm k v]
(assoc-in hm (map keyword (clojure.string/split k #"[\._]"))
(if (map? v)
(parse-keys-and-merge v)
v)))
{} hm))
This does not work for your hash-map because your hash-map does not clarify whether the entry for :foo
should be "baz"
or {:bar "baz", :bar2 "baz"}
. With a fixed hash-map it works:
(parse-keys-and-merge {"foo2_bar" "baz", "foo.bar2" "baz", "foo.bar" "baz"})
;; {:foo {:bar "baz", :bar2 "baz"}, :foo2 {:bar "baz"}}
Upvotes: 2