Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure pre/post-walk with path from root

Tags:

clojure

What I know

I'm familiar with clojure.walk/prewalk and clojure.walk/postwalk

What I want

I want something like clojure.walk/prewalk and clojure.walk/postwalk -- but I also want the path required to get to the node -- whereas prewalk/postwalk only gives me the nodes, w/o the actual path.

Example

So if we had a structure

[ {:cat "Garfield", :dog "DogeCoin"} [:a :b {:site "so"}]]

then I want my function called with args:

[] [ {:cat "Garfield", :dog "DogeCoin"} [:a :b {:site "so"}]]

[0] {:cat "Garfield", :dog "DogeCoin"}

[1] [:a :b {:site "so"}]

[0 :cat] "Garfield"

...

Question:

Is there a builtin for the above? where the processing function receives both the node and the path (from the root node) to the node)?

Thanks!

Possible Solution

(based on what fl00r suggested)

(defn pathwalk [f cur-path node]
  (let [f1 #(pathwalk f (conj cur-path %1) %2)]
    (f cur-path node)
    (cond
      (map? node) (map #(apply f1 %) node)
      (or (vector? node) (list? node)) (keep-indexed f1 node))))
like image 290
eav db Avatar asked Oct 29 '22 17:10

eav db


1 Answers

I suppose you also want 'pathwalk' to return something from function f similar to clojure.walk/prewalk and not rely on side-effect? E.g.

(prewalk #(if (= :a %) :c %) [:a :b])
=>
[:c :b]

If yes, then you can do this:

(defn pathwalk [f path e]
  (let [e' (f path e)]
    (cond
      (map? e')  (->> e'
                      (map (fn [[k x]] [k (pathwalk f (conj path k) x)]))
                      (into (empty e')))
      (coll? e') (->> e'
                      (map-indexed (fn [i x] (pathwalk f (conj path i) x)))
                      (into (empty e')))
      :else      e')))

Here is a test run:

(pathwalk #(do
             (println %1 %2)
             (if (= :a %2)
               :c
               %2))
          []
          [ {:cat "Garfield", :dog "DogeCoin"} [:a :b {:site "so"}]])

it will print:

[] [{:cat Garfield, :dog DogeCoin} [:a :b {:site so}]]
[0] {:cat Garfield, :dog DogeCoin}
[0 :cat] Garfield
[0 :dog] DogeCoin
[1] [:a :b {:site so}]
[1 0] :a
[1 1] :b
[1 2] {:site so}
[1 2 :site] so

and below data will be returned from the function:

[{:cat "Garfield", :dog "DogeCoin"} [:c :b {:site "so"}]]
like image 177
rmcv Avatar answered Jan 02 '23 21:01

rmcv