0
# Data Structure Utilities
1
2
Comprehensive utilities for traversing, transforming, and navigating complex data structures through specialized namespaces: clojure.walk for tree traversal, clojure.datafy for data transformation, and clojure.zip for functional tree editing.
3
4
## Capabilities
5
6
### Tree Walking (clojure.walk)
7
8
Generic tree traversal functions for recursively processing nested data structures.
9
10
```clojure { .api }
11
(clojure.walk/walk inner outer form)
12
;; Core traversal function - applies inner to each element, then outer to result
13
;; inner: function applied to each sub-element
14
;; outer: function applied to reconstructed structure
15
;; form: data structure to traverse
16
;; Returns: transformed data structure
17
18
(clojure.walk/prewalk f form)
19
;; Pre-order traversal - calls f on each sub-form before recursing
20
;; f: transformation function
21
;; form: data structure to traverse
22
;; Returns: transformed data structure
23
24
(clojure.walk/postwalk f form)
25
;; Post-order traversal - calls f on each sub-form after recursing
26
;; f: transformation function
27
;; form: data structure to traverse
28
;; Returns: transformed data structure
29
30
(clojure.walk/prewalk-demo form)
31
;; Demonstrates prewalk behavior by printing each walked form
32
;; form: data structure to walk and print
33
;; Returns: original form (unchanged)
34
35
(clojure.walk/postwalk-demo form)
36
;; Demonstrates postwalk behavior by printing each walked form
37
;; form: data structure to walk and print
38
;; Returns: original form (unchanged)
39
```
40
41
### Tree Replacement (clojure.walk)
42
43
Higher-level transformation functions built on tree walking.
44
45
```clojure { .api }
46
(clojure.walk/prewalk-replace smap form)
47
;; Pre-order replacement using substitution map
48
;; smap: map of {old-value new-value} replacements
49
;; form: data structure to transform
50
;; Returns: transformed data structure
51
52
(clojure.walk/postwalk-replace smap form)
53
;; Post-order replacement using substitution map
54
;; smap: map of {old-value new-value} replacements
55
;; form: data structure to transform
56
;; Returns: transformed data structure
57
58
(clojure.walk/keywordize-keys m)
59
;; Recursively transforms all map keys from strings to keywords
60
;; m: nested data structure containing maps
61
;; Returns: transformed structure with keyword keys
62
63
(clojure.walk/stringify-keys m)
64
;; Recursively transforms all map keys from keywords to strings
65
;; m: nested data structure containing maps
66
;; Returns: transformed structure with string keys
67
68
(clojure.walk/macroexpand-all form)
69
;; Recursively performs all possible macroexpansions in form
70
;; form: Clojure form potentially containing macros
71
;; Returns: fully macroexpanded form
72
```
73
74
### Data Transformation (clojure.datafy)
75
76
Protocol-based data transformation for converting objects to data representations.
77
78
```clojure { .api }
79
(clojure.datafy/datafy x)
80
;; Attempts to return x as data using clojure.core.protocols/datafy
81
;; x: object to transform to data representation
82
;; Returns: data representation of x with metadata about original
83
84
(clojure.datafy/nav coll k v)
85
;; Returns possibly transformed v in context of coll and key k
86
;; coll: containing collection
87
;; k: key/index context (or nil)
88
;; v: value to potentially transform
89
;; Returns: transformed value via clojure.core.protocols/nav
90
```
91
92
### Functional Zippers (clojure.zip)
93
94
Functional tree editing with location-aware navigation and modification.
95
96
#### Zipper Creation
97
98
```clojure { .api }
99
(clojure.zip/zipper branch? children make-node root)
100
;; Creates a new zipper structure for arbitrary trees
101
;; branch?: predicate fn - true if node can have children
102
;; children: fn returning seq of node's children
103
;; make-node: fn creating new branch node from existing node and children
104
;; root: root node of tree
105
;; Returns: zipper positioned at root
106
107
(clojure.zip/seq-zip root)
108
;; Returns zipper for nested sequences
109
;; root: root sequence
110
;; Returns: sequence zipper
111
112
(clojure.zip/vector-zip root)
113
;; Returns zipper for nested vectors
114
;; root: root vector
115
;; Returns: vector zipper
116
117
(clojure.zip/xml-zip root)
118
;; Returns zipper for XML elements (as from xml/parse)
119
;; root: root XML element
120
;; Returns: XML zipper
121
```
122
123
#### Zipper Navigation
124
125
```clojure { .api }
126
(clojure.zip/node loc)
127
;; Returns the node at current location
128
;; loc: zipper location
129
;; Returns: node at location
130
131
(clojure.zip/branch? loc)
132
;; Returns true if node at location is a branch
133
;; loc: zipper location
134
;; Returns: boolean
135
136
(clojure.zip/children loc)
137
;; Returns seq of children of branch node at location
138
;; loc: zipper location (must be branch)
139
;; Returns: seq of child nodes
140
141
(clojure.zip/down loc)
142
;; Returns location of first child, nil if no children
143
;; loc: zipper location
144
;; Returns: child location or nil
145
146
(clojure.zip/up loc)
147
;; Returns location of parent, nil if at top
148
;; loc: zipper location
149
;; Returns: parent location or nil
150
151
(clojure.zip/left loc)
152
;; Returns location of left sibling, nil if none
153
;; loc: zipper location
154
;; Returns: left sibling location or nil
155
156
(clojure.zip/right loc)
157
;; Returns location of right sibling, nil if none
158
;; loc: zipper location
159
;; Returns: right sibling location or nil
160
161
(clojure.zip/leftmost loc)
162
;; Returns location of leftmost sibling
163
;; loc: zipper location
164
;; Returns: leftmost sibling location
165
166
(clojure.zip/rightmost loc)
167
;; Returns location of rightmost sibling
168
;; loc: zipper location
169
;; Returns: rightmost sibling location
170
171
(clojure.zip/next loc)
172
;; Returns next location in depth-first walk, nil if end
173
;; loc: zipper location
174
;; Returns: next location or nil
175
176
(clojure.zip/prev loc)
177
;; Returns previous location in depth-first walk, nil if beginning
178
;; loc: zipper location
179
;; Returns: previous location or nil
180
```
181
182
#### Zipper Query Functions
183
184
```clojure { .api }
185
(clojure.zip/path loc)
186
;; Returns seq of nodes leading to this location
187
;; loc: zipper location
188
;; Returns: seq of ancestor nodes
189
190
(clojure.zip/lefts loc)
191
;; Returns seq of left siblings of this location
192
;; loc: zipper location
193
;; Returns: seq of left sibling nodes
194
195
(clojure.zip/rights loc)
196
;; Returns seq of right siblings of this location
197
;; loc: zipper location
198
;; Returns: seq of right sibling nodes
199
200
(clojure.zip/root loc)
201
;; Returns root node of entire tree
202
;; loc: zipper location
203
;; Returns: root node
204
205
(clojure.zip/end? loc)
206
;; Returns true if location represents end of depth-first walk
207
;; loc: zipper location
208
;; Returns: boolean
209
```
210
211
#### Zipper Editing
212
213
```clojure { .api }
214
(clojure.zip/replace loc node)
215
;; Returns new location with node replaced
216
;; loc: zipper location
217
;; node: replacement node
218
;; Returns: new location with replaced node
219
220
(clojure.zip/edit loc f & args)
221
;; Returns location with node replaced by (apply f node args)
222
;; loc: zipper location
223
;; f: transformation function
224
;; args: additional arguments to f
225
;; Returns: new location with edited node
226
227
(clojure.zip/insert-left loc item)
228
;; Returns location with item inserted as left sibling
229
;; loc: zipper location
230
;; item: node to insert
231
;; Returns: new location
232
233
(clojure.zip/insert-right loc item)
234
;; Returns location with item inserted as right sibling
235
;; loc: zipper location
236
;; item: node to insert
237
;; Returns: new location
238
239
(clojure.zip/insert-child loc item)
240
;; Returns location with item inserted as leftmost child
241
;; loc: zipper location (must be branch)
242
;; item: node to insert
243
;; Returns: new location
244
245
(clojure.zip/append-child loc item)
246
;; Returns location with item inserted as rightmost child
247
;; loc: zipper location (must be branch)
248
;; item: node to insert
249
;; Returns: new location
250
251
(clojure.zip/remove loc)
252
;; Returns location with node removed
253
;; loc: zipper location
254
;; Returns: new location with node removed
255
```
256
257
**Usage Examples:**
258
259
```clojure
260
;; Tree walking examples
261
(require '[clojure.walk :as walk])
262
263
;; Transform all numbers in nested structure
264
(walk/postwalk
265
#(if (number? %) (* % 2) %)
266
{:a 1 :b [2 3 {:c 4}]})
267
;; => {:a 2, :b [4 6 {:c 8}]}
268
269
;; String keys to keyword keys
270
(walk/keywordize-keys {"a" 1 "b" {"c" 2}})
271
;; => {:a 1, :b {:c 2}}
272
273
;; Replace specific values
274
(walk/postwalk-replace {'old 'new :a :replaced}
275
'(old data (:a value) old))
276
;; => (new data (:replaced value) new)
277
278
;; Functional zipper examples
279
(require '[clojure.zip :as zip])
280
281
;; Navigate nested vector structure
282
(def data [[1 2] [3 [4 5]] 6])
283
(def z (zip/vector-zip data))
284
285
;; Navigate to deeply nested value
286
(-> z
287
zip/down ; into first level: [1 2]
288
zip/right ; to [3 [4 5]]
289
zip/down ; into [3 [4 5]]
290
zip/right ; to [4 5]
291
zip/down ; into [4 5]
292
zip/right ; to 5
293
zip/node) ; => 5
294
295
;; Edit nested structure
296
(-> z
297
zip/down ; to [1 2]
298
zip/right ; to [3 [4 5]]
299
zip/down ; into [3 [4 5]]
300
(zip/edit inc) ; change 3 to 4
301
zip/root) ; => [[1 2] [4 [4 5]] 6]
302
303
;; Add new elements
304
(-> z
305
zip/down ; to [1 2]
306
(zip/insert-right [7 8]) ; insert new vector
307
zip/root) ; => [[1 2] [7 8] [3 [4 5]] 6]
308
309
;; Tree traversal with zippers
310
(defn find-in-tree [pred z]
311
(loop [loc z]
312
(cond
313
(zip/end? loc) nil
314
(pred (zip/node loc)) loc
315
:else (recur (zip/next loc)))))
316
317
;; Find first even number
318
(let [z (zip/vector-zip [1 [2 3] [4 [5 6]]])]
319
(when-let [loc (find-in-tree even? z)]
320
(zip/node loc))) ; => 2
321
322
;; Data transformation with datafy
323
(require '[clojure.datafy :as d])
324
325
;; Transform exception to data
326
(try
327
(/ 1 0)
328
(catch Exception e
329
(d/datafy e)))
330
;; => returns map with exception details
331
332
;; Working with refs and atoms
333
(let [a (atom {:count 42})]
334
(d/datafy a)) ; => [{\count 42}] with metadata
335
```