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.
3
Answers
Here’s another way to do it using
postwalk
for the whole traversal, rather than using it only fordefault-value
replacement:Trying to read heavily nested code makes my head hurt. It is worse when the answer is something of a “force-fit” with
postwalk
, which does things in a sort of “inside out” manner. Also, usingpartition-all
is a bit of a waste, since we need to discard any pairs with two non-vectors.To me, the most natural solution is a simple top-down recursion. The only problem is that we don’t know in advance if we need to remove one or two items from the head of the input sequence. Thus, we can’t use a simple
for
loop ormap
.So, just write it as a straightforward recursion, and use an
if
to determine whether we consume 1 or 2 items from the head of the list.:dummy-value
to make a map entry.as the value in the map entry.
The code:
with result:
I added
keywordize-keys
at then end just to make the result a little more “Clojurey”.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.