Search code examples
clojure

Translating vector into map


I've got this list of fields (that's Facebook's graph API fields list).

["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"]

I want to generate a map out of it. The convention is following, if after a key vector follows, then its an inner object for the key. Example vector could be represented as a map as:

{"a" "value"
 "b" {"c" {"t" "value"} "d" "value"}
 "e" {"f" "value"}
 "g" "value"}

So I have this solution so far

(defn traverse
  [data]
  (mapcat (fn [[left right]]
            (if (vector? right)
              (let [traversed (traverse right)]
                (mapv (partial into [left]) traversed))
              [[right]]))
          (partition 2 1 (into [nil] data))))

(defn facebook-fields->map
  [fields default-value]
  (->> fields
       (traverse)
       (reduce #(assoc-in %1 %2 nil) {})
       (clojure.walk/postwalk #(or % default-value))))

(let [data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"]]
  (facebook-fields->map data "value"))
#=> {"a" "value", "b" {"c" {"t" "value"}, "d" "value"}, "e" {"f" "value"}, "g" "value"}

But it is fat and difficult to follow. I am wondering if there is a more elegant solution.


Solution

  • Since you're asking for a cleaner solution as opposed to a solution, and because I thought it was a neat little problem, here's another one.

    (defn facebook-fields->map [coll]
      (into {}
            (keep (fn [[x y]]
                    (when-not (vector? x)
                      (if (vector? y)
                        [x (facebook-fields->map y)]
                        [x "value"]))))
            (partition-all 2 1 coll)))