(zipper branch? children make-node root)
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)