@johanatan of course, navigators are infinitely composable
will be easier to show you the most elegant approach if you give an example of the input and desired output
Hi @nathanmarz and all, I’m beginning to use Specter everywhere 🙂. I’m still struggling with some admittedly basic recursion/tree transformation though. For example, here’s my input:
(def tree [0
[1]
[2 [3]]])
The tree is arbitrarily nested, each node is a vector of type [root & children]
(e.g. [0] is the tree root, [1] and [2] its immediate children, [1] and [2] are siblings). My goal is to transform the tree into the following structure. In other words, each node under the root becomes an :li item. Also if a node has children, I’d like to “wrap” them with a :ul container. It should be simple, but I can’t get my head around it yet. But maybe you can help me on this one?
[:ul
[:li {:id "1"}]
[:li {:id "2"}
[:ul
[:li {:id "3"}]]]]
@cperrone here's one way to do it:
(def tree [0
[1]
[2 [3]]])
(def NODE-CHILDREN
(path INDEXED-VALS #(not= 0 (first %)) LAST))
(def NODES (recursive-path [] p
(continue-then-stay
NODE-CHILDREN
p)))
(defn wrap [children id _]
(if (empty? children)
[:li {:id id}]
[:li {:id id} (setval BEFORE-ELEM :ul children)]
)
)
(nth
(transform
[NODES
(collect NODE-CHILDREN)
(collect-one FIRST)
]
wrap
tree
)
2)
ah brilliant, thank you @nathanmarz. I much appreciate it. I suspected I needed to collect values along the way, but I wasn’t sure how (I need to play more with that). Great answer. Would this be more typical of a zipper use, by the way? At some stage I wondered if walking with
(defn my-zipper [v]
(z/zipper next rest my-make-node v))
(obviously then wrapped by the specter zipper :D) would have been easier for this case.@cperrone zippers aren't needed in this case
it's best to avoid using zippers since they add a lot of overhead
I’m still studying your approach to the problem, in particular, I was surprised by the setval within the transform fn
your use case does two things: wrap in :li
and wrap in :ul
the inner setval prepends to a vector without changing the type (as cons
would do)
yes. I guess I also need to check the result of the overall transform “before” you take the nth index.
yea, the handling of the root is special with how you laid out the problem
yes, maybe some slight tweaking on the input could help (as it’s often the case)
if you have the control, then making the structure of the data as consistent as possible helps a lot
eh eh I do. I though I was consistent though. [root children children] all the way no?
yea, in this case it's the output that's slightly inconsistent
true, the wrapping is slightly tricky
i read somewhere that zippers are pretty lightweight by the way… I don’t particularly like the imperative navigation that it often leads to though.
"lightweight" is relative
compared to a straight reduce
it's not
it's not just performance, they also add conceptual overhead
ah ah I guess so, especially in clojurescript, everything counts, so it’s a matter of tradeoffs
indeed