1
votes

I want to use clojure.zip to walk through this tree and print the node and its parent. I am having trouble getting the parent. For example the parent of :e is :b.

;;
;;     :a
;;     / \
;;   :b  :c
;;   /\    \
;; :d :e   :f
;;
(def example [:a [:b [:d] [:e]] [:c [:f]]])
(def z (zip/vector-zip example))
(def locs (take-while (complement zip/end?) (iterate zip/next z)))

(defn parent-of [loc]
  (when-let [parent-loc (-> loc zip/up zip/left)]
    (zip/node parent-loc)))

(defn visit-all []
  (doseq [loc locs]
    (let [node (zip/node loc)]
      (when (keyword? node)
        (println node "has parent" (parent-of loc))))))

This is the result:

:a has parent nil
:b has parent :a
:d has parent :b
:e has parent [:d]
:c has parent [:b [:d] [:e]]
:f has parent :c

I could keep improving the parent-of function - my next thought would be to go to the left-most node. There will be an algorithm that will return the correct answer from all locations - however going about it this way seems like quite a lot of work for a common requirement.

Is there a better approach I should be taking?

Edit This question is not about clojure.walk or Spector. I am looking for an answer that uses clojure.zip and gives the parent as I defined it in the question, which is simply the keyword above the loc. So if the loc given to parent-of is :f I would expect it to return :c.

If someone can tell me, as a comment, that zippers are not really used anymore, and say clojure.walk or Spector is the current best practice way to go for navigating trees, then that would help.

2
Can you provide sample inputs and outputs for the function you are trying to implement? - OlegTheCat
Possible duplicate of Clojure - walk with path - nha

2 Answers

2
votes

Why are you using zip/left? The parent of the node is exactly the node above it, not a node above it and to the left for some reason. Removing that is all you need to do.

0
votes

This is my own answer:

(defn parent-of [loc]
  (when-let [parent-loc (-> loc zip/up zip/up first)]
    (zip/node parent-loc)))

It gives the correct output in all cases:

:a has parent nil
:b has parent :a
:d has parent :b
:e has parent :b
:c has parent :a
:f has parent :c