ClojureDocs

Nav

Namespaces

postwalk

clojure.walk

Available since 1.1
  • (postwalk f form)
Performs a depth-first, post-order traversal of form.  Calls f on
each sub-form, uses f's return value in place of the original.
Recognizes all Clojure data structures. Consumes seqs as with doall.
5 Examples
(require '[clojure.walk :refer [postwalk]])
(let [counter (atom -1)
      line-counter (atom 0)
      print-touch (fn [x]
                    (print (swap! line-counter inc) ":" (pr-str x) "→ "))
      change (fn [x]
               (let [new-x (swap! counter inc)]
                 (prn new-x)
                 [new-x x]))]
  (postwalk (fn [x]
              (print-touch x)
              (change x))
            {:a 1 :b 2}))

;; printed output:

1 : :a  0
2 : 1  1
3 : [[0 :a] [1 1]]  2
4 : :b  3
5 : 2  4
6 : [[3 :b] [4 2]]  5
7 : {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]}  6

;; returned value:

[6 {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]}]
;;example of removing namespaces from all keys in a nested data structure
(def thing {:page/tags [{:tag/category "lslsls"}]})
(postwalk #(if (keyword? %) (keyword (name %)) %) thing)
{:tags [{:category "lslsls"}]}
(use 'clojure.walk)

;;example of evaluating an expression tree, starting at the leaves
(def expression-tree
  {:function +
   :children
    [1 {:function *
        :children [2 6]}]})

(defn evaluate [node]
  (if-let [f (:function node)]
    (apply f (:children node))
    node))

(postwalk evaluate expression-tree)

=> 13
;; an example to show the differences between postwalk and prewalk
;; (see the counterpart at prewalk)

(let [counter     (atom 0)
      print-touch (fn [x]
                    (print (swap! counter inc) ":" (pr-str x) "→ "))
      change-type (fn [x]
                    (let [new-x (if (vector? x)
                                  (apply list x)
                                  (str x))]
                      (prn new-x)
                      new-x))]
  (clojure.walk/postwalk (fn [x]
                           (print-touch x)
                           (change-type x))
                         [:a [:ba :bb] :c]))

;; printed output:

1 : :a  ":a"
2 : :ba  ":ba"
3 : :bb  ":bb"
4 : [":ba" ":bb"]  (":ba" ":bb")
5 : :c  ":c"
6 : [":a" (":ba" ":bb") ":c"]  (":a" (":ba" ":bb") ":c")

;; returned value:

=> (":a" (":ba" ":bb") ":c")
;; Recursively sort nested map
(require '[clojure.walk :as w])

(def m {:b 2, :c 3, :a 1, :d {:x 10, :c 3, :a 11, :b 22}})

(w/postwalk (fn [x] (if (map? x) (into (sorted-map) x) x)) m)
;;=> {:a 1, :b 2, :c 3, :d {:a 11, :b 22, :c 3, :x 10}}
See Also

Like postwalk, but does pre-order traversal.

Added by srid

Demonstrates the behavior of postwalk by printing each form as it is walked. Returns form.

Added by dansalmo

Recursively transforms form by replacing keys in smap with their values. Like clojure/replace but...

Added by dansalmo
4 Notes
    By , created 7.1 years ago

    As of 1.9.0, postwalk passes the k/v pairs of a map to f not, as one might expect, as type clojure.lang.MapEntry but as clojure.lang.PersistentVector (JIRA). As a result, f cannot distinguish k/v pairs from other two-element vectors.

    By , created 6.4 years ago

    Alex Miller's article "Tree visitors in Clojure" might be helpful in understanding general tree traversal and the usage of clojure.walk/postwalk.

    By , created 4.1 years ago

    As of 1.9.0, postwalk passes the k/v pairs of a map to f not, as one might expect, as type clojure.lang.MapEntry but as clojure.lang.PersistentVector (JIRA). As a result, f cannot distinguish k/v pairs from other two-element vectors.

    This was fixed in 1.10.0, and one can use (map-entry? v) to distinguish a k/v pair from a vector when walking.

    By , created 3.5 years ago

    Updated link to "Tree visitors in Clojure", mentioned above (PDF):

    https://insideclojure.org/images/j-treevisit-pdf.pdf