0
# Tree Iterators
1
2
ES6-compatible iterators for all traversal patterns with efficient lazy evaluation.
3
4
## Capabilities
5
6
### Children Iterator
7
8
Iterate over all children of a parent object.
9
10
```javascript { .api }
11
/**
12
* Iterate over all children of the given parent object
13
* Time Complexity: O(1) for a single iteration
14
* @param {Object} parent - Parent object
15
* @param {Object} [options] - Iterator options
16
* @param {boolean} [options.reverse=false] - Iterate in reverse order
17
* @returns {Iterator<Object>} ES6 iterator over children
18
*/
19
childrenIterator(parent: Object, options?: { reverse?: boolean }): Iterator<Object>;
20
```
21
22
### Previous Siblings Iterator
23
24
Iterate over all previous siblings of an object (in reverse tree order).
25
26
```javascript { .api }
27
/**
28
* Iterate over all previous siblings of the given object
29
* Time Complexity: O(1) for a single iteration
30
* @param {Object} object - Reference object
31
* @returns {Iterator<Object>} ES6 iterator over previous siblings
32
*/
33
previousSiblingsIterator(object: Object): Iterator<Object>;
34
```
35
36
### Next Siblings Iterator
37
38
Iterate over all next siblings of an object (in tree order).
39
40
```javascript { .api }
41
/**
42
* Iterate over all next siblings of the given object
43
* Time Complexity: O(1) for a single iteration
44
* @param {Object} object - Reference object
45
* @returns {Iterator<Object>} ES6 iterator over next siblings
46
*/
47
nextSiblingsIterator(object: Object): Iterator<Object>;
48
```
49
50
### Ancestors Iterator
51
52
Iterate over all inclusive ancestors of an object.
53
54
```javascript { .api }
55
/**
56
* Iterate over all inclusive ancestors of the given object
57
* Time Complexity: O(1) for a single iteration
58
* @param {Object} object - Starting object
59
* @returns {Iterator<Object>} ES6 iterator over ancestors (including the object itself)
60
*/
61
ancestorsIterator(object: Object): Iterator<Object>;
62
```
63
64
### Tree Iterator
65
66
Iterate over all descendants of an object (in tree order).
67
68
```javascript { .api }
69
/**
70
* Iterate over all descendants of the given object in tree order
71
* Time Complexity: O(n) worst case for single iteration, O(n) amortized when completing
72
* @param {Object} root - Root object of tree/subtree
73
* @param {Object} [options] - Iterator options
74
* @param {boolean} [options.reverse=false] - Iterate in reverse tree order
75
* @returns {Iterator<Object>} ES6 iterator over tree descendants
76
*/
77
treeIterator(root: Object, options?: { reverse?: boolean }): Iterator<Object>;
78
```
79
80
## Iterator Protocol
81
82
All iterators implement the ES6 iterator protocol and are compatible with:
83
84
```javascript { .api }
85
// Iterator interface
86
interface Iterator<T> {
87
next(): IteratorResult<T>;
88
[Symbol.iterator](): Iterator<T>;
89
}
90
91
interface IteratorResult<T> {
92
done: boolean;
93
value: T;
94
}
95
```
96
97
## Usage Examples
98
99
### Basic Iterator Usage
100
101
```javascript
102
const SymbolTree = require("symbol-tree");
103
const tree = new SymbolTree();
104
105
// Build a tree structure
106
const parent = { name: "parent" };
107
const child1 = { name: "child1" };
108
const child2 = { name: "child2" };
109
const child3 = { name: "child3" };
110
111
tree.appendChild(parent, child1);
112
tree.appendChild(parent, child2);
113
tree.appendChild(parent, child3);
114
115
// Iterate over children using for...of
116
for (const child of tree.childrenIterator(parent)) {
117
console.log(child.name);
118
}
119
// Output: child1, child2, child3
120
121
// Iterate in reverse
122
for (const child of tree.childrenIterator(parent, { reverse: true })) {
123
console.log(child.name);
124
}
125
// Output: child3, child2, child1
126
```
127
128
### Manual Iterator Control
129
130
```javascript
131
// Manual iterator control
132
const childIterator = tree.childrenIterator(parent);
133
134
let result = childIterator.next();
135
while (!result.done) {
136
console.log("Child:", result.value.name);
137
result = childIterator.next();
138
}
139
140
// Or using the iterator protocol
141
const children = [...tree.childrenIterator(parent)];
142
console.log(children.map(c => c.name)); // ["child1", "child2", "child3"]
143
```
144
145
### Sibling Iteration
146
147
```javascript
148
// Iterate over next siblings
149
for (const sibling of tree.nextSiblingsIterator(child1)) {
150
console.log("Next sibling:", sibling.name);
151
}
152
// Output: Next sibling: child2, Next sibling: child3
153
154
// Iterate over previous siblings
155
for (const sibling of tree.previousSiblingsIterator(child3)) {
156
console.log("Previous sibling:", sibling.name);
157
}
158
// Output: Previous sibling: child2, Previous sibling: child1
159
```
160
161
### Tree Structure Iteration
162
163
```javascript
164
// Build larger tree
165
const grandchild1 = { name: "grandchild1" };
166
const grandchild2 = { name: "grandchild2" };
167
tree.appendChild(child1, grandchild1);
168
tree.appendChild(child2, grandchild2);
169
170
// Iterate over entire tree
171
console.log("Forward tree iteration:");
172
for (const node of tree.treeIterator(parent, { reverse: false })) {
173
console.log(node.name);
174
}
175
// Output: parent, child1, grandchild1, child2, grandchild2, child3
176
177
console.log("Reverse tree iteration:");
178
for (const node of tree.treeIterator(parent, { reverse: true })) {
179
console.log(node.name);
180
}
181
// Output: child3, grandchild2, child2, grandchild1, child1, parent
182
```
183
184
### Ancestor Iteration
185
186
```javascript
187
// Iterate up the ancestor chain
188
console.log("Ancestors of grandchild1:");
189
for (const ancestor of tree.ancestorsIterator(grandchild1)) {
190
console.log(ancestor.name);
191
}
192
// Output: grandchild1, child1, parent
193
```
194
195
### Advanced Iterator Patterns
196
197
```javascript
198
// Find first child matching condition
199
function findChild(parent, predicate) {
200
for (const child of tree.childrenIterator(parent)) {
201
if (predicate(child)) {
202
return child;
203
}
204
}
205
return null;
206
}
207
208
const firstChildWithName = findChild(parent, child =>
209
child.name.includes("2")
210
);
211
console.log(firstChildWithName?.name); // "child2"
212
213
// Collect nodes at specific depth
214
function getNodesAtDepth(root, targetDepth) {
215
const nodes = [];
216
217
for (const node of tree.treeIterator(root)) {
218
const ancestors = [...tree.ancestorsIterator(node)];
219
const depth = ancestors.length - 1;
220
221
if (depth === targetDepth) {
222
nodes.push(node);
223
}
224
}
225
226
return nodes;
227
}
228
229
const depthOneNodes = getNodesAtDepth(parent, 1);
230
console.log(depthOneNodes.map(n => n.name)); // ["child1", "child2", "child3"]
231
```
232
233
### Functional Programming with Iterators
234
235
```javascript
236
// Convert iterators to arrays for functional operations
237
const childNames = [...tree.childrenIterator(parent)]
238
.map(child => child.name)
239
.filter(name => name.includes("child"))
240
.sort();
241
242
console.log(childNames); // ["child1", "child2", "child3"]
243
244
// Reduce over tree
245
const treeStats = [...tree.treeIterator(parent)]
246
.reduce((stats, node) => {
247
stats.total++;
248
if (tree.hasChildren(node)) {
249
stats.containers++;
250
} else {
251
stats.leaves++;
252
}
253
return stats;
254
}, { total: 0, containers: 0, leaves: 0 });
255
256
console.log(treeStats); // { total: 6, containers: 3, leaves: 3 }
257
```
258
259
### Performance-Conscious Patterns
260
261
```javascript
262
// Early termination (more efficient than converting to array first)
263
function findFirstLeafNode(root) {
264
for (const node of tree.treeIterator(root)) {
265
if (!tree.hasChildren(node)) {
266
return node; // Stop immediately when found
267
}
268
}
269
return null;
270
}
271
272
// Process in chunks to avoid memory pressure
273
function processTreeInChunks(root, chunkSize = 100) {
274
let chunk = [];
275
276
for (const node of tree.treeIterator(root)) {
277
chunk.push(node);
278
279
if (chunk.length >= chunkSize) {
280
processChunk(chunk);
281
chunk = []; // Clear for next chunk
282
}
283
}
284
285
// Process remaining items
286
if (chunk.length > 0) {
287
processChunk(chunk);
288
}
289
}
290
291
function processChunk(nodes) {
292
// Do something with the chunk of nodes
293
console.log(`Processing chunk of ${nodes.length} nodes`);
294
}
295
```
296
297
### Iterator Composition
298
299
```javascript
300
// Combine multiple iterators
301
function* getAllSiblings(node) {
302
// Yield previous siblings in reverse order
303
const prevSiblings = [...tree.previousSiblingsIterator(node)].reverse();
304
for (const sibling of prevSiblings) {
305
yield sibling;
306
}
307
308
// Yield the node itself
309
yield node;
310
311
// Yield next siblings
312
for (const sibling of tree.nextSiblingsIterator(node)) {
313
yield sibling;
314
}
315
}
316
317
// Usage
318
console.log("All siblings of child2:");
319
for (const sibling of getAllSiblings(child2)) {
320
console.log(sibling.name);
321
}
322
// Output: child1, child2, child3
323
324
// Create custom tree walker
325
function* walkTreeWithCallback(root, callback) {
326
for (const node of tree.treeIterator(root)) {
327
const result = callback(node);
328
if (result) {
329
yield { node, result };
330
}
331
}
332
}
333
334
// Usage
335
const results = [...walkTreeWithCallback(parent, node => {
336
if (node.name.includes("child")) {
337
return `Found child: ${node.name}`;
338
}
339
})];
340
341
console.log(results);
342
```
343
344
### DOM-Style Iterator Usage
345
346
```javascript
347
// Simulate DOM traversal patterns
348
function* getElementsOfType(root, tagName) {
349
for (const node of tree.treeIterator(root)) {
350
if (node.tagName === tagName) {
351
yield node;
352
}
353
}
354
}
355
356
function getParentElements(node) {
357
const parents = [];
358
359
for (const ancestor of tree.ancestorsIterator(node)) {
360
if (ancestor !== node && ancestor.nodeType === "element") {
361
parents.push(ancestor);
362
}
363
}
364
365
return parents;
366
}
367
368
// Find next element sibling
369
function getNextElementSibling(node) {
370
for (const sibling of tree.nextSiblingsIterator(node)) {
371
if (sibling.nodeType === "element") {
372
return sibling;
373
}
374
}
375
return null;
376
}
377
```