Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flattening a map by join the keys

Tags:

clojure

Given a nested map with only keyword keys such as {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5}, how can I implement flatten-map so that (flatten-map {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5} "-") produces something like {:foo-bar 1 :foo-baz [2 3] :foo-qux-quux 4 :corge 5}.

My best attempt is:

(defn flatten-map
  ([form separator] (flatten-map form separator nil))
  ([form separator prefix]
  (if (map? form)
    (into {} (map (fn [[k v]]
                    [(keyword (str prefix (name k)))
                     (flatten-map v separator (str prefix (name k) separator))])
                  form))
    form)))

As you can see I can't get flatten-map to select only the "leaves".

like image 876
Martin Harrigan Avatar asked Jul 27 '13 19:07

Martin Harrigan


1 Answers

(defn flatten-map
  ([form separator]
     (into {} (flatten-map form separator nil)))
  ([form separator pre]
     (mapcat (fn [[k v]]
               (let [prefix (if pre (str pre separator (name k)) (name k))]
                 (if (map? v)
                   (flatten-map v separator prefix)
                   [[(keyword prefix) v]])))
               form)))

you were unconditionally creating new key / value pairs, even when the value was to be expanded, so I switched map to mapcat so that a result could be "subsumed" into the top level (this also required splitting the (into {} ...) into the top level version of the form, since we don't actually want any maps anywhere but the top level of the output).

Here is how it works with your example:

user> (flatten-map {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5} "-")
{:foo-bar 1, :foo-qux-quux 4, :foo-baz [2 3], :corge 5}
like image 62
noisesmith Avatar answered Oct 25 '22 19:10

noisesmith