Creates a new zipper structure. branch? is a fn that, given a node, returns true if can have children, even if it currently doesn't. children is a fn that, given a branch node, returns a seq of its children. make-node is a fn that, given an existing node and a seq of children, returns a new branch node with the supplied children. root is the root node.
;; Some clojure.zip functions will overwrite clojure.core's definitions (use 'clojure.zip) ;; You may wish to require :as in order to avoid the above (require '[clojure.zip :as z]) ;; For the purposes of keeping the examples that follow clean, ;; assume we have taken the former route: (use 'clojure.zip) (use 'clojure.pprint) (def p pprint) user> (def z [[1 2 3] [4 [5 6] 7] [8 9]]) #'user/z user> (def zp (zipper vector? seq (fn [_ c] c) z)) #'user/zp user> zp [[[1 2 3] [4 [5 6] 7] [8 9]] nil] user=> (p (-> zp down)) [[1 2 3] {:l [], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([4 [5 6] 7] [8 9])}] user> (first (-> zp down)) [1 2 3] user=> (p (-> zp down right)) [[4 [5 6] 7] {:l [[1 2 3]], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([8 9])}] user> (first (-> zp down right)) [4 [5 6] 7] user=> (p (-> zp down right down right)) [[5 6] {:l [4], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]] [4 [5 6] 7]], :ppath {:l [[1 2 3]], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([8 9])}, :r (7)}] user=> (p (-> zp down right down right down)) [5 {:l [], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]] [4 [5 6] 7] [5 6]], :ppath {:l [4], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]] [4 [5 6] 7]], :ppath {:l [[1 2 3]], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([8 9])}, :r (7)}, :r (6)}] user=> (p (-> zp down right down right (replace "hello"))) ["hello" {:changed? true, :l [4], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]] [4 [5 6] 7]], :ppath {:l [[1 2 3]], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([8 9])}, :r (7)}] user=> (p (-> zp down right down right (replace "hello") up)) [(4 "hello" 7) {:changed? true, :l [[1 2 3]], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([8 9])}] user=> (p (-> zp down right down right (replace "hello") up root)) ([1 2 3] (4 "hello" 7) [8 9])
(require '[clojure.zip :as zip]) ;; Adds zip support for maps. ;; (Source: http://stackoverflow.com/a/15020649/42188) (defn map-zipper [m] (zip/zipper (fn [x] (or (map? x) (map? (nth x 1)))) (fn [x] (seq (if (map? x) x (nth x 1)))) (fn [x children] (if (map? x) (into {} children) (assoc x 1 (into {} children)))) m)) (def m {:a 3 :b {:x true :y false} :c 4}) ;; Note that hash-maps are not ordered: (-> (map-zipper m) zip/down zip/right zip/node) ;;=> [:b {:y false, :x true}] ;; Treat nodes as [key value] pairs: (-> (map-zipper m) zip/down (zip/edit (fn [[k v]] [k (inc v)])) zip/root) ;;=> {:c 5, :b {:y false, :x true}, :a 3}
;; A version of zipper that allows mixing maps and vectors ;; Note that it traverses map entries too (require '[clojure.zip :as z]) (defn map-vec-zipper [m] (z/zipper (fn [x] (or (map? x) (sequential? x))) seq (fn [p xs] (if (isa? (type p) clojure.lang.MapEntry) (into [] xs) (into (empty p) xs))) m)) (-> (map-vec-zipper [{1 [21 22] 3 [4]}]) z/down (z/edit assoc :e 99) z/down ;; Note that the map does not guarantee particular entries ordering. z/down ;; Getting into map entry. z/next (z/edit conj 77) z/root) ;;=> [{1 [21 22 77], 3 [4], :e 99}]
;; Get sequence of all visited nodes (require '[clojure.zip :as z]) (->> (z/vector-zip [[1 2] 3 [[4 5]]]) (iterate z/next) (take-while #(not (z/end? %))) ;; Zipper's "end of iteration" condition. (map z/node)) ;;=> ([[1 2] 3 [[4 5]]] ;; [1 2] ;; 1 2 ;; 3 ;; [[4 5]] ;; [4 5] ;; 4 5)
For more info on zippers:
Tutorials on zippers:
Brian Marick: "Editing" trees in Clojure with clojure.zip
Alex Miller: Zippers with records in Clojure
To work with nested structures you can also use