I have an HTML represented in a weird form (it is much easier to work with than a regular nested one):
[{:text "5d" :em true :strong true}
{:text "xx" :em true}
{:text "damn" :em true :strong true}
{:text "c6"}
{:text "qwe" :em true}
{:text "asd"}
{:text "qqq" :em true :strong true}]
I need to convert it to a Hiccup-like one:
[[:em
[:strong "5d"]
"xx"
[:strong "damn"]]
"c6"
[:em "qwe"]
"asd"
[:strong [:em "qqq"]]]
The best implementation I came up with is:
(defn wrap-tags [states nodes]
(if (seq states)
(reduce
(fn [nodes state]
[(into [state] nodes)])
nodes states)
nodes))
(defn p->tags
([data]
(p->tags data #{} [] []))
([[node & rest] state waiting result]
(let [new-state (set (keys (dissoc node :text)))
closed (clojure.set/difference state new-state)
waiting (conj (wrap-tags closed waiting) (:text node))
result (if-not (seq new-state)
(into result waiting)
result)
waiting (if-not (seq new-state) [] waiting)]
(if (seq rest)
(p->tags rest new-state waiting result)
(if (seq waiting)
(into result (wrap-tags new-state waiting))
result)))))
It's not working properly though, it doesn't handle the case when :strong appears (it has no idea how much of "waiting" nodes it should wrap, and wraps all of them - but I have no ideas how to track this). It looks a bit ugly to me as well, but that's less annoying. :) What it returns for my case right now is:
[[:em
[:strong
[:strong "5d"]
"xx"
"damn"]]
"c6"
[:em "qwe"]
"asd"
[:em [:strong "qqq"]]]
I would love to hear any ideas how to improve my code.
{:text "5d" :em true :strong true}results in[:em [:strong]], last one{:text "qqq" :em true :strong true} => [:strong [:em]]emis the tag containing all three tags, and in second case I don't really care about order. No real difference if it's<strong><em>qqq</em></strong>or<em><strong>qqq</strong></em>