Skip to content

Instantly share code, notes, and snippets.

@SegFaultAX
Created April 16, 2014 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SegFaultAX/10939429 to your computer and use it in GitHub Desktop.
Save SegFaultAX/10939429 to your computer and use it in GitHub Desktop.
Dotted path expansion in Clojure
(defn deep-merge-with
"Like merge-with, but preserves shared key paths, recursively."
[f & maps]
(letfn [(merge-elem [m e]
(let [k (key e) v (val e) src (m k)]
(if src
(if (and (map? src) (map? v))
(assoc m k (merge2 src v))
(assoc m k (f src v)))
(assoc m k v))))
(merge2 [d1 d2]
(reduce merge-elem (or d1 {}) (seq d2)))]
(reduce merge2 maps)))
(defn expand-key
"Converts a key/value pair from the form
[:a.b.c.d 1]
to a map with nested keys
{:a {:b {:c {:d 1}}}}"
[[k v]]
(let [parts (clojure.string/split (name k) #"\.")
path (mapv keyword parts)]
(assoc-in {} path v)))
(defn expand-keys
"Expands all keys in a given map using `expand-key`"
[d]
(apply deep-merge-with into (map expand-key d)))
(defn expand-paths
"Walks a recursive data structure, expanding all key paths using
`clojure.walk/prewalk` with `expand-keys`"
[d]
(clojure.walk/prewalk #(if (map? %) (expand-keys %) %) d))
;; Example usage
(comment
(def q {:query.filtered.query.bool.should
[{:query_string.default_field "_all"
:query_string.query "keywords"}
{:multi_match.query 123
:multi_match.type "most_fields"
:multi_match.fields "derp"}]
:query.filtered.query.filter []})
(=
(expand-paths q)
{:query
{:filtered
{:query
{:bool
{:should
[{:query_string {:query "keywords", :default_field "_all"}}
{:multi_match
{:type "most_fields", :query 123, :fields "derp"}}]},
:filter []}}}})
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment