tree-processor 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1026 -0
- package/README.md +53 -91
- package/dist/index.d.ts.map +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -7
package/README.en.md
ADDED
|
@@ -0,0 +1,1026 @@
|
|
|
1
|
+
# tree-processor
|
|
2
|
+
|
|
3
|
+
<div align="right">
|
|
4
|
+
|
|
5
|
+
[English](https://github.com/knott11/tree-processor/blob/main/README.en.md) | [中文](https://github.com/knott11/tree-processor/blob/main/README.md)
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div align="center">
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
A lightweight tree-structured data processing utility library written in TypeScript, supporting tree-shaking, with each format bundle size approximately **6-7 KB** (ESM: 6.39 KB, CJS: 6.64 KB, UMD: 6.68 KB).
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
## 📋 Table of Contents
|
|
23
|
+
|
|
24
|
+
- [Features](#-features)
|
|
25
|
+
- [Use Cases](#-use-cases)
|
|
26
|
+
- [Installation](#-installation)
|
|
27
|
+
- [Quick Start](#-quick-start)
|
|
28
|
+
- [API Documentation](#-api-documentation)
|
|
29
|
+
- [Traversal Methods](#traversal-methods)
|
|
30
|
+
- [Search Methods](#search-methods)
|
|
31
|
+
- [Access Methods](#access-methods)
|
|
32
|
+
- [Modification Methods](#modification-methods)
|
|
33
|
+
- [Query Methods](#query-methods)
|
|
34
|
+
- [Validation Methods](#validation-methods)
|
|
35
|
+
- [Custom Field Names](#custom-field-names)
|
|
36
|
+
- [Testing](#testing)
|
|
37
|
+
- [Development](#development)
|
|
38
|
+
|
|
39
|
+
## ✨ Features
|
|
40
|
+
|
|
41
|
+
- **Lightweight** - Each format bundle size is only 6-7 KB (ESM: 6.39 KB, CJS: 6.64 KB, UMD: 6.68 KB), minimal impact on project size
|
|
42
|
+
- **Tree-shaking Support** - Supports on-demand imports, only bundles the code you actually use, further reducing bundle size
|
|
43
|
+
- **Full TypeScript Support** - Provides complete type definitions and IntelliSense, improving development experience
|
|
44
|
+
- **Flexible Custom Field Names** - Supports custom children and id field names, adapting to various data structures
|
|
45
|
+
- **Zero Dependencies** - No external dependencies, ready to use out of the box, no need to worry about dependency conflicts
|
|
46
|
+
- **Comprehensive Test Coverage** - Contains 290 test cases with 99%+ test coverage (99.67% statement coverage, 99.32% branch coverage, 100% function coverage), covering basic functionality, edge cases, error handling, and complex scenarios
|
|
47
|
+
- **Rich API** - Provides 30+ methods, including array-like APIs (map, filter, find, some, every, includes, at, indexOf, etc.), and tree-specific operations (get parent/child nodes, depth calculation, data validation, etc.), covering complete scenarios for traversal, search, modification, and validation
|
|
48
|
+
|
|
49
|
+
**Supported methods:** mapTree, forEachTree, filterTree, findTree, pushTree, unshiftTree, popTree, shiftTree, someTree, everyTree, includesTree, atTree, indexOfTree, atIndexOfTree, dedupTree, removeTree, getParentTree, getChildrenTree, getSiblingsTree, getNodeDepthMap, getNodeDepth, isLeafNode, isRootNode, isEmptyTreeData, isEmptySingleTreeData, isTreeData, isSingleTreeData, isValidTreeNode, isTreeNodeWithCircularCheck, isSafeTreeDepth. The last parameter of each method can customize the property names for children and id.
|
|
50
|
+
|
|
51
|
+
### 💡 Use Cases
|
|
52
|
+
|
|
53
|
+
- **Navigation Systems** - Multi-level menus, route configuration expansion, collapse, search, and filtering
|
|
54
|
+
- **File Systems** - File directory traversal, search, move, and delete
|
|
55
|
+
- **Permission Systems** - Organizational structure, role permission tree structure management and validation
|
|
56
|
+
- **Framework Development** - Component trees, route trees, and other tree structure construction and management
|
|
57
|
+
- **Data Management** - Category management, comment systems, tree selectors, and other data operations
|
|
58
|
+
|
|
59
|
+
## 📦 Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install tree-processor
|
|
63
|
+
# or
|
|
64
|
+
yarn add tree-processor
|
|
65
|
+
# or
|
|
66
|
+
pnpm add tree-processor
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 🚀 Quick Start
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import { mapTree, findTree, filterTree } from 'tree-processor'
|
|
73
|
+
|
|
74
|
+
const treeData = [
|
|
75
|
+
{
|
|
76
|
+
id: 1,
|
|
77
|
+
name: 'node1',
|
|
78
|
+
children: [
|
|
79
|
+
{ id: 2, name: 'node2' },
|
|
80
|
+
{ id: 3, name: 'node3' },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
// Get all node names
|
|
86
|
+
const names = mapTree(treeData, (node) => node.name)
|
|
87
|
+
console.log(names) // ['node1', 'node2', 'node3']
|
|
88
|
+
|
|
89
|
+
// Find a node
|
|
90
|
+
const node = findTree(treeData, (n) => n.id === 2)
|
|
91
|
+
console.log(node) // { id: 2, name: 'node2' }
|
|
92
|
+
|
|
93
|
+
// Filter nodes
|
|
94
|
+
const filtered = filterTree(treeData, (n) => n.id > 1)
|
|
95
|
+
console.log(filtered) // [{ id: 2, name: 'node2' }, { id: 3, name: 'node3' }]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 📖 API Documentation
|
|
99
|
+
|
|
100
|
+
### Import Methods
|
|
101
|
+
|
|
102
|
+
#### Default Import (Recommended for scenarios requiring multiple methods)
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// ES Module
|
|
106
|
+
import t from 'tree-processor'
|
|
107
|
+
|
|
108
|
+
// CommonJS
|
|
109
|
+
const t = require('tree-processor')
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Named Import (Recommended for scenarios requiring only a few methods, supports tree-shaking)
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// ES Module - Named import of individual methods
|
|
116
|
+
import { mapTree, filterTree, findTree } from 'tree-processor'
|
|
117
|
+
|
|
118
|
+
// ES Module - Import types
|
|
119
|
+
import type { TreeNode, TreeData, FieldNames } from 'tree-processor'
|
|
120
|
+
|
|
121
|
+
// CommonJS - Named import
|
|
122
|
+
const { mapTree, filterTree } = require('tree-processor')
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Sample Data
|
|
126
|
+
|
|
127
|
+
The following sample data will be used for demonstrations of all subsequent methods:
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const treeData = [
|
|
131
|
+
{
|
|
132
|
+
id: 1,
|
|
133
|
+
name: 'node1',
|
|
134
|
+
children: [
|
|
135
|
+
{
|
|
136
|
+
id: 2,
|
|
137
|
+
name: 'node2',
|
|
138
|
+
children: [
|
|
139
|
+
{ id: 4, name: 'node4' },
|
|
140
|
+
{ id: 5, name: 'node5' },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 3,
|
|
145
|
+
name: 'node3',
|
|
146
|
+
children: [{ id: 6, name: 'node6' }],
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Traversal Methods
|
|
156
|
+
|
|
157
|
+
### mapTree
|
|
158
|
+
|
|
159
|
+
Traverse tree-structured data and execute a callback function for each node, returning a mapped array.
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
// Get all node names
|
|
163
|
+
const nodeNames = t.mapTree(treeData, (node) => node.name)
|
|
164
|
+
console.log(nodeNames) // ['node1', 'node2', 'node4', 'node5', 'node3', 'node6']
|
|
165
|
+
|
|
166
|
+
// Get all node IDs
|
|
167
|
+
const nodeIds = t.mapTree(treeData, (node) => node.id)
|
|
168
|
+
console.log(nodeIds) // [1, 2, 4, 5, 3, 6]
|
|
169
|
+
|
|
170
|
+
// Modify node data
|
|
171
|
+
const modifiedNodes = t.mapTree(treeData, (node) => ({
|
|
172
|
+
...node,
|
|
173
|
+
label: node.name
|
|
174
|
+
}))
|
|
175
|
+
console.log(modifiedNodes) // Returns a new array containing the label field
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### forEachTree
|
|
179
|
+
|
|
180
|
+
Traverse tree-structured data and execute a callback function for each node. Unlike mapTree, this doesn't return a value and has better performance, suitable for scenarios where you only need to traverse without returning results.
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
// Traverse all nodes and print
|
|
184
|
+
t.forEachTree(treeData, (node) => {
|
|
185
|
+
console.log(node)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// Modify node properties
|
|
189
|
+
t.forEachTree(treeData, (node) => {
|
|
190
|
+
node.visited = true
|
|
191
|
+
node.timestamp = Date.now()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// Count nodes
|
|
195
|
+
let nodeCount = 0
|
|
196
|
+
t.forEachTree(treeData, () => {
|
|
197
|
+
nodeCount++
|
|
198
|
+
})
|
|
199
|
+
console.log(nodeCount) // Total number of nodes
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Search Methods
|
|
205
|
+
|
|
206
|
+
### filterTree
|
|
207
|
+
|
|
208
|
+
Filter tree-structured data and return nodes that meet the conditions.
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
// Filter nodes whose names contain 'node'
|
|
212
|
+
const filteredNodes = t.filterTree(treeData, (node) => {
|
|
213
|
+
return node.name.includes('node')
|
|
214
|
+
})
|
|
215
|
+
console.log(filteredNodes) // Returns an array of nodes that meet the conditions
|
|
216
|
+
|
|
217
|
+
// Filter nodes with ID greater than 2
|
|
218
|
+
const nodesWithLargeId = t.filterTree(treeData, (node) => node.id > 2)
|
|
219
|
+
console.log(nodesWithLargeId) // Returns an array of nodes with ID greater than 2
|
|
220
|
+
|
|
221
|
+
// Filter nodes without children (leaf nodes)
|
|
222
|
+
const leafNodes = t.filterTree(treeData, (node) => {
|
|
223
|
+
return !node.children || node.children.length === 0
|
|
224
|
+
})
|
|
225
|
+
console.log(leafNodes) // Returns all leaf nodes
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### findTree
|
|
229
|
+
|
|
230
|
+
Find the first node in tree-structured data that meets the condition. Returns null if not found.
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
// Find node with ID 2
|
|
234
|
+
const foundNode = t.findTree(treeData, (node) => node.id === 2)
|
|
235
|
+
console.log(foundNode) // Returns the found node object, or null if not found
|
|
236
|
+
|
|
237
|
+
// Find node with name 'node3'
|
|
238
|
+
const node3 = t.findTree(treeData, (node) => node.name === 'node3')
|
|
239
|
+
console.log(node3) // { id: 3, name: 'node3', children: [...] }
|
|
240
|
+
|
|
241
|
+
// Find a non-existent node
|
|
242
|
+
const nodeNotFound = t.findTree(treeData, (node) => node.id === 999)
|
|
243
|
+
console.log(nodeNotFound) // null
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### includesTree
|
|
247
|
+
|
|
248
|
+
Check if tree-structured data contains a node with the specified ID.
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
const nodeId = 2
|
|
252
|
+
const hasNode = t.includesTree(treeData, nodeId)
|
|
253
|
+
|
|
254
|
+
console.log(hasNode) // true means it contains the node, false means it doesn't
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### someTree
|
|
258
|
+
|
|
259
|
+
Check if there exists a node in tree-structured data that meets the condition. Returns true if at least one node meets the condition.
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
// Check if there exists a node with name 'node2'
|
|
263
|
+
const hasNode2 = t.someTree(treeData, node => node.name === 'node2')
|
|
264
|
+
console.log(hasNode2) // true
|
|
265
|
+
|
|
266
|
+
// Check if there exists a node with ID greater than 10
|
|
267
|
+
const hasLargeId = t.someTree(treeData, node => node.id > 10)
|
|
268
|
+
console.log(hasLargeId) // false
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### everyTree
|
|
272
|
+
|
|
273
|
+
Check if all nodes in tree-structured data meet the condition. Returns true only if all nodes meet the condition.
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
// Check if all node IDs are greater than 0
|
|
277
|
+
const allIdsPositive = t.everyTree(treeData, node => node.id > 0)
|
|
278
|
+
console.log(allIdsPositive) // true
|
|
279
|
+
|
|
280
|
+
// Check if all nodes have a name property
|
|
281
|
+
const allHaveName = t.everyTree(treeData, node => node.name)
|
|
282
|
+
console.log(allHaveName) // Returns true or false based on actual data
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Access Methods
|
|
288
|
+
|
|
289
|
+
### atTree
|
|
290
|
+
|
|
291
|
+
Get a node by parent node ID and child node index. Supports negative indices, same as the array at method. Returns null if not found.
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
// Get the first child node (index 0) of the node with ID 1
|
|
295
|
+
const firstChildNode = t.atTree(treeData, 1, 0)
|
|
296
|
+
console.log(firstChildNode) // Returns the first child node
|
|
297
|
+
|
|
298
|
+
// Get the last child node (negative index)
|
|
299
|
+
const lastChildNode = t.atTree(treeData, 1, -1)
|
|
300
|
+
console.log(lastChildNode) // Returns the last child node
|
|
301
|
+
|
|
302
|
+
// Index out of range returns null
|
|
303
|
+
const nodeNotFound = t.atTree(treeData, 1, 10)
|
|
304
|
+
console.log(nodeNotFound) // null
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### indexOfTree
|
|
308
|
+
|
|
309
|
+
Returns an array representing the index path from the root node to the node with the targetId. Returns null if not found. The return value can be passed to the second parameter of atIndexOfTree for value retrieval.
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
// Get the index path of the node with ID 4
|
|
313
|
+
const nodePath = t.indexOfTree(treeData, 4)
|
|
314
|
+
console.log(nodePath) // [0, 0, 0] means root node -> first child -> first child
|
|
315
|
+
|
|
316
|
+
// Node not found returns null
|
|
317
|
+
const pathNotFound = t.indexOfTree(treeData, 999)
|
|
318
|
+
console.log(pathNotFound) // null
|
|
319
|
+
|
|
320
|
+
// Use with atIndexOfTree
|
|
321
|
+
const indexPath = t.indexOfTree(treeData, 4)
|
|
322
|
+
const nodeByPath = t.atIndexOfTree(treeData, indexPath)
|
|
323
|
+
console.log(nodeByPath) // Gets the node with ID 4
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### atIndexOfTree
|
|
327
|
+
|
|
328
|
+
Get a node by index path. Returns null if the path is invalid or out of range.
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
// Get node by index path
|
|
332
|
+
const nodeByIndexPath = t.atIndexOfTree(treeData, [0, 1, 0])
|
|
333
|
+
console.log(nodeByIndexPath) // Returns the node object at the corresponding path
|
|
334
|
+
|
|
335
|
+
// Use with indexOfTree
|
|
336
|
+
const targetPath = t.indexOfTree(treeData, 4)
|
|
337
|
+
const targetNode = t.atIndexOfTree(treeData, targetPath)
|
|
338
|
+
console.log(targetNode) // Gets the node with ID 4
|
|
339
|
+
|
|
340
|
+
// Invalid path returns null
|
|
341
|
+
const invalidPath = t.atIndexOfTree(treeData, [999])
|
|
342
|
+
console.log(invalidPath) // null
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Modification Methods
|
|
348
|
+
|
|
349
|
+
### pushTree
|
|
350
|
+
|
|
351
|
+
Add a child node to the end under the specified node. Returns true if successful, false if the target node is not found.
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
// Add a new child node under the node with ID 1
|
|
355
|
+
const addSuccess = t.pushTree(treeData, 1, { id: 7, name: 'node7' })
|
|
356
|
+
console.log(addSuccess) // true
|
|
357
|
+
console.log(treeData) // New node has been added to the end of the children array
|
|
358
|
+
|
|
359
|
+
// Try to add under a non-existent node
|
|
360
|
+
const addFailed = t.pushTree(treeData, 999, { id: 8, name: 'node8' })
|
|
361
|
+
console.log(addFailed) // false, target node not found
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### unshiftTree
|
|
365
|
+
|
|
366
|
+
Add a child node to the beginning under the specified node. Returns true if successful, false if the target node is not found.
|
|
367
|
+
|
|
368
|
+
```javascript
|
|
369
|
+
// Add a new child node to the beginning under the node with ID 1
|
|
370
|
+
const unshiftSuccess = t.unshiftTree(treeData, 1, { id: 7, name: 'node7' })
|
|
371
|
+
console.log(unshiftSuccess) // true
|
|
372
|
+
console.log(treeData) // New node has been added to the beginning of the children array
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### popTree
|
|
376
|
+
|
|
377
|
+
Remove the last child node under the specified node. Returns the removed node, or false if the node doesn't exist or has no children.
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
// Remove the last child node under the node with ID 1
|
|
381
|
+
const removedNode = t.popTree(treeData, 1)
|
|
382
|
+
console.log(removedNode) // Returns the removed node object, or false
|
|
383
|
+
|
|
384
|
+
// Try to remove from a non-existent node
|
|
385
|
+
const popFailed = t.popTree(treeData, 999)
|
|
386
|
+
console.log(popFailed) // false
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### shiftTree
|
|
390
|
+
|
|
391
|
+
Remove the first child node under the specified node. Returns the removed node, or false if the node doesn't exist or has no children.
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
// Remove the first child node under the node with ID 1
|
|
395
|
+
const shiftedNode = t.shiftTree(treeData, 1)
|
|
396
|
+
console.log(shiftedNode) // Returns the removed node object, or false
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### removeTree
|
|
400
|
+
|
|
401
|
+
Remove the node with the specified ID from tree-structured data, including root nodes and child nodes.
|
|
402
|
+
|
|
403
|
+
```javascript
|
|
404
|
+
const nodeIdToRemove = 2
|
|
405
|
+
const removeSuccess = t.removeTree(treeData, nodeIdToRemove)
|
|
406
|
+
|
|
407
|
+
console.log(removeSuccess) // true means successful removal, false means node not found
|
|
408
|
+
console.log(treeData) // Tree structure after removal
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### dedupTree
|
|
412
|
+
|
|
413
|
+
Tree-structured object array deduplication method that removes duplicate nodes based on the specified key. Keeps the first occurrence of the node.
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
// Deduplicate based on id field
|
|
417
|
+
const uniqueTreeData = t.dedupTree(treeData, 'id')
|
|
418
|
+
console.log(uniqueTreeData) // Returns deduplicated tree-structured data
|
|
419
|
+
|
|
420
|
+
// Deduplicate based on name field
|
|
421
|
+
const uniqueByNameTree = t.dedupTree(treeData, 'name')
|
|
422
|
+
console.log(uniqueByNameTree) // Returns data deduplicated by name
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Query Methods
|
|
428
|
+
|
|
429
|
+
### getParentTree
|
|
430
|
+
|
|
431
|
+
Get the parent node of the specified node. Returns null if the node is a root node or not found.
|
|
432
|
+
|
|
433
|
+
```javascript
|
|
434
|
+
// Get the parent node of the node with ID 2
|
|
435
|
+
const parentNode = t.getParentTree(treeData, 2)
|
|
436
|
+
console.log(parentNode) // Returns the parent node object { id: 1, name: 'node1', ... }
|
|
437
|
+
|
|
438
|
+
// Root nodes have no parent, returns null
|
|
439
|
+
const rootParentNode = t.getParentTree(treeData, 1)
|
|
440
|
+
console.log(rootParentNode) // null
|
|
441
|
+
|
|
442
|
+
// Node not found returns null
|
|
443
|
+
const parentNotFound = t.getParentTree(treeData, 999)
|
|
444
|
+
console.log(parentNotFound) // null
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### getChildrenTree
|
|
448
|
+
|
|
449
|
+
Get all direct child nodes of the specified node. Returns an empty array if the node is not found or has no children.
|
|
450
|
+
|
|
451
|
+
```javascript
|
|
452
|
+
// Get all child nodes of the node with ID 1
|
|
453
|
+
const children = t.getChildrenTree(treeData, 1)
|
|
454
|
+
console.log(children) // Returns child node array [{ id: 2, ... }, { id: 3, ... }]
|
|
455
|
+
|
|
456
|
+
// Node has no children, returns empty array
|
|
457
|
+
const emptyChildren = t.getChildrenTree(treeData, 4)
|
|
458
|
+
console.log(emptyChildren) // []
|
|
459
|
+
|
|
460
|
+
// Node not found returns empty array
|
|
461
|
+
const notFound = t.getChildrenTree(treeData, 999)
|
|
462
|
+
console.log(notFound) // []
|
|
463
|
+
|
|
464
|
+
// Support custom field names
|
|
465
|
+
const customTree = [
|
|
466
|
+
{
|
|
467
|
+
nodeId: 1,
|
|
468
|
+
name: 'root',
|
|
469
|
+
subNodes: [
|
|
470
|
+
{ nodeId: 2, name: 'child1' },
|
|
471
|
+
{ nodeId: 3, name: 'child2' },
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
];
|
|
475
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
476
|
+
const customChildren = t.getChildrenTree(customTree, 1, fieldNames)
|
|
477
|
+
console.log(customChildren) // Returns child node array
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### getSiblingsTree
|
|
481
|
+
|
|
482
|
+
Get all sibling nodes of the specified node (including itself). Returns an empty array if the node is not found. Sibling nodes of root nodes are other root nodes.
|
|
483
|
+
|
|
484
|
+
```javascript
|
|
485
|
+
// Get all sibling nodes (including itself) of the node with ID 2
|
|
486
|
+
const siblings = t.getSiblingsTree(treeData, 2)
|
|
487
|
+
console.log(siblings) // Returns sibling node array [{ id: 2, ... }, { id: 3, ... }]
|
|
488
|
+
|
|
489
|
+
// Sibling nodes of root nodes are other root nodes
|
|
490
|
+
const multiRoot = [
|
|
491
|
+
{ id: 1, children: [{ id: 2 }] },
|
|
492
|
+
{ id: 3, children: [{ id: 4 }] },
|
|
493
|
+
];
|
|
494
|
+
const rootSiblings = t.getSiblingsTree(multiRoot, 1)
|
|
495
|
+
console.log(rootSiblings) // Returns all root nodes [{ id: 1, ... }, { id: 3, ... }]
|
|
496
|
+
|
|
497
|
+
// Node not found returns empty array
|
|
498
|
+
const notFound = t.getSiblingsTree(treeData, 999)
|
|
499
|
+
console.log(notFound) // []
|
|
500
|
+
|
|
501
|
+
// Support custom field names
|
|
502
|
+
const customTree = [
|
|
503
|
+
{
|
|
504
|
+
nodeId: 1,
|
|
505
|
+
name: 'root',
|
|
506
|
+
subNodes: [
|
|
507
|
+
{ nodeId: 2, name: 'child1' },
|
|
508
|
+
{ nodeId: 3, name: 'child2' },
|
|
509
|
+
{ nodeId: 4, name: 'child3' },
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
];
|
|
513
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
514
|
+
const customSiblings = t.getSiblingsTree(customTree, 2, fieldNames)
|
|
515
|
+
console.log(customSiblings) // Returns sibling node array (including itself)
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### getNodeDepthMap
|
|
519
|
+
|
|
520
|
+
Returns a dictionary where keys represent node ids and values represent which layer the node is in the data. Depth starts from 1, root nodes have depth 1.
|
|
521
|
+
|
|
522
|
+
```javascript
|
|
523
|
+
// Get depth map of all nodes
|
|
524
|
+
const nodeDepthMap = t.getNodeDepthMap(treeData)
|
|
525
|
+
console.log(nodeDepthMap) // { 1: 1, 2: 2, 3: 2, 4: 3, 5: 3, 6: 3 }
|
|
526
|
+
|
|
527
|
+
// Get depth of a specific node
|
|
528
|
+
const node2Depth = nodeDepthMap[2]
|
|
529
|
+
console.log(node2Depth) // 2
|
|
530
|
+
|
|
531
|
+
// Empty tree returns empty object
|
|
532
|
+
const emptyDepthMap = t.getNodeDepthMap([])
|
|
533
|
+
console.log(emptyDepthMap) // {}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### getNodeDepth
|
|
537
|
+
|
|
538
|
+
Get the depth of the specified node. Depth starts from 1, root nodes have depth 1.
|
|
539
|
+
|
|
540
|
+
```javascript
|
|
541
|
+
// Get depth of root node
|
|
542
|
+
const rootDepth = t.getNodeDepth(treeData, 1)
|
|
543
|
+
console.log(rootDepth) // 1
|
|
544
|
+
|
|
545
|
+
// Get depth of child node
|
|
546
|
+
const childDepth = t.getNodeDepth(treeData, 2)
|
|
547
|
+
console.log(childDepth) // 2
|
|
548
|
+
|
|
549
|
+
// Get depth of deep node
|
|
550
|
+
const deepDepth = t.getNodeDepth(treeData, 4)
|
|
551
|
+
console.log(deepDepth) // 3
|
|
552
|
+
|
|
553
|
+
// Node not found returns null
|
|
554
|
+
const notFound = t.getNodeDepth(treeData, 999)
|
|
555
|
+
console.log(notFound) // null
|
|
556
|
+
|
|
557
|
+
// Support custom field names
|
|
558
|
+
const customTree = [
|
|
559
|
+
{
|
|
560
|
+
nodeId: 1,
|
|
561
|
+
name: 'root',
|
|
562
|
+
subNodes: [
|
|
563
|
+
{ nodeId: 2, name: 'child' },
|
|
564
|
+
],
|
|
565
|
+
},
|
|
566
|
+
];
|
|
567
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
568
|
+
const depth = t.getNodeDepth(customTree, 2, fieldNames)
|
|
569
|
+
console.log(depth) // 2
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Difference from getNodeDepthMap:**
|
|
573
|
+
- `getNodeDepthMap` - Batch get depth of all nodes (calculates all nodes at once)
|
|
574
|
+
- `getNodeDepth` - Only get depth of a single node (only calculates the target node, more efficient)
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## Validation Methods
|
|
579
|
+
|
|
580
|
+
### isLeafNode
|
|
581
|
+
|
|
582
|
+
Check if a node is a leaf node (has no children). Lightweight method that only checks the node itself, doesn't traverse the tree.
|
|
583
|
+
|
|
584
|
+
```javascript
|
|
585
|
+
// Nodes without children field are leaf nodes
|
|
586
|
+
const leafNode1 = { id: 1, name: 'node1' };
|
|
587
|
+
console.log(t.isLeafNode(leafNode1)) // true
|
|
588
|
+
|
|
589
|
+
// Nodes with empty children array are leaf nodes
|
|
590
|
+
const leafNode2 = { id: 2, name: 'node2', children: [] };
|
|
591
|
+
console.log(t.isLeafNode(leafNode2)) // true
|
|
592
|
+
|
|
593
|
+
// Nodes with children are not leaf nodes
|
|
594
|
+
const parentNode = {
|
|
595
|
+
id: 3,
|
|
596
|
+
name: 'node3',
|
|
597
|
+
children: [{ id: 4, name: 'node4' }],
|
|
598
|
+
};
|
|
599
|
+
console.log(t.isLeafNode(parentNode)) // false
|
|
600
|
+
|
|
601
|
+
// Use in filterTree (filter out all leaf nodes)
|
|
602
|
+
const leafNodes = t.filterTree(treeData, (node) => t.isLeafNode(node))
|
|
603
|
+
console.log(leafNodes) // Returns all leaf nodes
|
|
604
|
+
|
|
605
|
+
// Use in forEachTree
|
|
606
|
+
t.forEachTree(treeData, (node) => {
|
|
607
|
+
if (t.isLeafNode(node)) {
|
|
608
|
+
console.log('Leaf node:', node.name)
|
|
609
|
+
}
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
// Support custom field names
|
|
613
|
+
const customNode = {
|
|
614
|
+
nodeId: 1,
|
|
615
|
+
name: 'node1',
|
|
616
|
+
subNodes: [],
|
|
617
|
+
};
|
|
618
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
619
|
+
console.log(t.isLeafNode(customNode, fieldNames)) // true
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Difference from existing methods:**
|
|
623
|
+
- `isLeafNode` - Only checks a single node, lightweight (O(1)), suitable for use during traversal
|
|
624
|
+
- `getChildrenTree` - Gets child node array, requires passing tree and nodeId, needs to find the node (O(n))
|
|
625
|
+
|
|
626
|
+
### isRootNode
|
|
627
|
+
|
|
628
|
+
Check if a node is a root node (has no parent). Root nodes are top-level nodes in the tree-structured data array.
|
|
629
|
+
|
|
630
|
+
```javascript
|
|
631
|
+
// Check root node
|
|
632
|
+
const treeData = [
|
|
633
|
+
{
|
|
634
|
+
id: 1,
|
|
635
|
+
name: 'root1',
|
|
636
|
+
children: [{ id: 2, name: 'child1' }],
|
|
637
|
+
},
|
|
638
|
+
];
|
|
639
|
+
console.log(t.isRootNode(treeData, 1)) // true
|
|
640
|
+
console.log(t.isRootNode(treeData, 2)) // false
|
|
641
|
+
|
|
642
|
+
// Multiple root nodes case
|
|
643
|
+
const multiRoot = [
|
|
644
|
+
{ id: 1, name: 'root1' },
|
|
645
|
+
{ id: 2, name: 'root2' },
|
|
646
|
+
{ id: 3, name: 'root3' },
|
|
647
|
+
];
|
|
648
|
+
console.log(t.isRootNode(multiRoot, 1)) // true
|
|
649
|
+
console.log(t.isRootNode(multiRoot, 2)) // true
|
|
650
|
+
console.log(t.isRootNode(multiRoot, 3)) // true
|
|
651
|
+
|
|
652
|
+
// Use during traversal
|
|
653
|
+
t.forEachTree(treeData, (node) => {
|
|
654
|
+
if (t.isRootNode(treeData, node.id)) {
|
|
655
|
+
console.log('Root node:', node.name)
|
|
656
|
+
}
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
// Support custom field names
|
|
660
|
+
const customTree = [
|
|
661
|
+
{
|
|
662
|
+
nodeId: 1,
|
|
663
|
+
name: 'root1',
|
|
664
|
+
subNodes: [{ nodeId: 2, name: 'child1' }],
|
|
665
|
+
},
|
|
666
|
+
];
|
|
667
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
668
|
+
console.log(t.isRootNode(customTree, 1, fieldNames)) // true
|
|
669
|
+
console.log(t.isRootNode(customTree, 2, fieldNames)) // false
|
|
670
|
+
|
|
671
|
+
// Returns false when node doesn't exist
|
|
672
|
+
console.log(t.isRootNode(treeData, 999)) // false
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Difference from existing methods:**
|
|
676
|
+
- `isRootNode` - Semantic method that directly returns boolean value
|
|
677
|
+
- `getParentTree` - Returns parent node object, need to check if it's null
|
|
678
|
+
- `getNodeDepth` - Returns depth, need to check if it equals 1
|
|
679
|
+
|
|
680
|
+
### isEmptyTreeData
|
|
681
|
+
|
|
682
|
+
Check if tree-structured data (array) is empty. Empty arrays, null, and undefined are all considered empty. This function supports the fieldNames parameter to maintain API consistency, but the parameter has no effect (because it only checks if the array is empty, doesn't access children or id fields).
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
// Check if tree-structured data is empty
|
|
686
|
+
const isEmptyTree = t.isEmptyTreeData(treeData)
|
|
687
|
+
console.log(isEmptyTree) // false (has data)
|
|
688
|
+
|
|
689
|
+
// Empty array returns true
|
|
690
|
+
const isEmptyArray = t.isEmptyTreeData([])
|
|
691
|
+
console.log(isEmptyArray) // true
|
|
692
|
+
|
|
693
|
+
// null or undefined returns true
|
|
694
|
+
const isNullTree = t.isEmptyTreeData(null)
|
|
695
|
+
console.log(isNullTree) // true
|
|
696
|
+
|
|
697
|
+
// Support fieldNames parameter (maintains API consistency, but has no effect)
|
|
698
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
699
|
+
const isEmptyWithFieldNames = t.isEmptyTreeData(treeData, fieldNames)
|
|
700
|
+
console.log(isEmptyWithFieldNames) // false (same result as not passing fieldNames)
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### isEmptySingleTreeData
|
|
704
|
+
|
|
705
|
+
Check if a single tree-structured data is empty. If the data is not valid single tree-structured data, has no children field, or children is an empty array, it is considered empty. If there are child nodes (children array is not empty), even if the child nodes themselves are empty, the tree is not empty.
|
|
706
|
+
|
|
707
|
+
```javascript
|
|
708
|
+
// No children field, considered empty
|
|
709
|
+
const tree1 = { id: 1, name: 'node1' };
|
|
710
|
+
const isEmpty1 = t.isEmptySingleTreeData(tree1)
|
|
711
|
+
console.log(isEmpty1) // true
|
|
712
|
+
|
|
713
|
+
// children is empty array, considered empty
|
|
714
|
+
const tree2 = {
|
|
715
|
+
id: 1,
|
|
716
|
+
name: 'node1',
|
|
717
|
+
children: [],
|
|
718
|
+
};
|
|
719
|
+
const isEmpty2 = t.isEmptySingleTreeData(tree2)
|
|
720
|
+
console.log(isEmpty2) // true
|
|
721
|
+
|
|
722
|
+
// Has child nodes, not empty
|
|
723
|
+
const tree3 = {
|
|
724
|
+
id: 1,
|
|
725
|
+
name: 'node1',
|
|
726
|
+
children: [
|
|
727
|
+
{ id: 2, name: 'node2' },
|
|
728
|
+
],
|
|
729
|
+
};
|
|
730
|
+
const isEmpty3 = t.isEmptySingleTreeData(tree3)
|
|
731
|
+
console.log(isEmpty3) // false
|
|
732
|
+
|
|
733
|
+
// Has child nodes, even if child nodes themselves are empty, tree is not empty
|
|
734
|
+
const tree4 = {
|
|
735
|
+
id: 1,
|
|
736
|
+
name: 'node1',
|
|
737
|
+
children: [
|
|
738
|
+
{ id: 2, name: 'node2', children: [] },
|
|
739
|
+
{ id: 3, name: 'node3' }, // no children field
|
|
740
|
+
],
|
|
741
|
+
};
|
|
742
|
+
const isEmpty4 = t.isEmptySingleTreeData(tree4)
|
|
743
|
+
console.log(isEmpty4) // false (because there are child nodes, even if they are empty)
|
|
744
|
+
|
|
745
|
+
// Support custom field names
|
|
746
|
+
const customTree = {
|
|
747
|
+
nodeId: 1,
|
|
748
|
+
name: 'node1',
|
|
749
|
+
subNodes: [],
|
|
750
|
+
};
|
|
751
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
752
|
+
const isEmptyCustom = t.isEmptySingleTreeData(customTree, fieldNames)
|
|
753
|
+
console.log(isEmptyCustom) // true
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
### isTreeData
|
|
757
|
+
|
|
758
|
+
Check if data is tree-structured data (array). Tree-structured data must be an array, and each element in the array must be valid single tree-structured data.
|
|
759
|
+
|
|
760
|
+
```javascript
|
|
761
|
+
// Valid tree-structured data (forest)
|
|
762
|
+
const forest = [
|
|
763
|
+
{
|
|
764
|
+
id: 1,
|
|
765
|
+
name: 'node1',
|
|
766
|
+
children: [{ id: 2, name: 'node2' }],
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
id: 3,
|
|
770
|
+
name: 'node3',
|
|
771
|
+
children: [{ id: 4, name: 'node4' }],
|
|
772
|
+
},
|
|
773
|
+
];
|
|
774
|
+
console.log(t.isTreeData(forest)) // true
|
|
775
|
+
|
|
776
|
+
// Empty array is also valid tree-structured data (empty forest)
|
|
777
|
+
console.log(t.isTreeData([])) // true
|
|
778
|
+
|
|
779
|
+
// Single object is not tree-structured data (should use isSingleTreeData)
|
|
780
|
+
console.log(t.isTreeData({ id: 1 })) // false
|
|
781
|
+
|
|
782
|
+
// Array contains non-tree-structured elements, returns false
|
|
783
|
+
const invalidForest = [
|
|
784
|
+
{ id: 1, children: [{ id: 2 }] },
|
|
785
|
+
'not a tree', // invalid element
|
|
786
|
+
];
|
|
787
|
+
console.log(t.isTreeData(invalidForest)) // false
|
|
788
|
+
|
|
789
|
+
// null or undefined are not valid tree-structured data
|
|
790
|
+
console.log(t.isTreeData(null)) // false
|
|
791
|
+
console.log(t.isTreeData(undefined)) // false
|
|
792
|
+
|
|
793
|
+
// Support custom field names
|
|
794
|
+
const customForest = [
|
|
795
|
+
{
|
|
796
|
+
nodeId: 1,
|
|
797
|
+
name: 'node1',
|
|
798
|
+
subNodes: [{ nodeId: 2, name: 'node2' }],
|
|
799
|
+
},
|
|
800
|
+
];
|
|
801
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
802
|
+
console.log(t.isTreeData(customForest, fieldNames)) // true
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### isSingleTreeData
|
|
806
|
+
|
|
807
|
+
Check if data is single tree-structured data (single object). Tree-structured data must be an object (cannot be an array, null, undefined, or primitive type). If a children field exists, it must be an array type, and all child nodes will be recursively checked.
|
|
808
|
+
|
|
809
|
+
```javascript
|
|
810
|
+
// Valid single tree-structured data
|
|
811
|
+
const tree = {
|
|
812
|
+
id: 1,
|
|
813
|
+
name: 'node1',
|
|
814
|
+
children: [
|
|
815
|
+
{ id: 2, name: 'node2' },
|
|
816
|
+
{ id: 3, name: 'node3' },
|
|
817
|
+
],
|
|
818
|
+
};
|
|
819
|
+
const isValid = t.isSingleTreeData(tree)
|
|
820
|
+
console.log(isValid) // true
|
|
821
|
+
|
|
822
|
+
// No children field is also valid (only root node)
|
|
823
|
+
const singleNode = { id: 1, name: 'node1' }
|
|
824
|
+
console.log(t.isSingleTreeData(singleNode)) // true
|
|
825
|
+
|
|
826
|
+
// Array is not single tree-structured data
|
|
827
|
+
console.log(t.isSingleTreeData([])) // false
|
|
828
|
+
|
|
829
|
+
// null or undefined are not valid tree-structured data
|
|
830
|
+
console.log(t.isSingleTreeData(null)) // false
|
|
831
|
+
console.log(t.isSingleTreeData(undefined)) // false
|
|
832
|
+
|
|
833
|
+
// children cannot be null
|
|
834
|
+
const invalidTree = { id: 1, children: null }
|
|
835
|
+
console.log(t.isSingleTreeData(invalidTree)) // false
|
|
836
|
+
|
|
837
|
+
// Support custom field names
|
|
838
|
+
const customTree = {
|
|
839
|
+
nodeId: 1,
|
|
840
|
+
name: 'node1',
|
|
841
|
+
subNodes: [{ nodeId: 2, name: 'node2' }],
|
|
842
|
+
};
|
|
843
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
844
|
+
console.log(t.isSingleTreeData(customTree, fieldNames)) // true
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### isValidTreeNode
|
|
848
|
+
|
|
849
|
+
Check if a single node is a valid tree node structure (lightweight, doesn't recursively check child nodes). Only checks the structure of the node itself, doesn't check child nodes.
|
|
850
|
+
|
|
851
|
+
```javascript
|
|
852
|
+
// Valid tree node (has children array)
|
|
853
|
+
const node1 = {
|
|
854
|
+
id: 1,
|
|
855
|
+
name: 'node1',
|
|
856
|
+
children: [{ id: 2 }],
|
|
857
|
+
};
|
|
858
|
+
console.log(t.isValidTreeNode(node1)) // true
|
|
859
|
+
|
|
860
|
+
// Valid tree node (no children field)
|
|
861
|
+
const node2 = { id: 1, name: 'node1' };
|
|
862
|
+
console.log(t.isValidTreeNode(node2)) // true
|
|
863
|
+
|
|
864
|
+
// Invalid tree node (children is not an array)
|
|
865
|
+
const invalidNode = {
|
|
866
|
+
id: 1,
|
|
867
|
+
children: 'not an array',
|
|
868
|
+
};
|
|
869
|
+
console.log(t.isValidTreeNode(invalidNode)) // false
|
|
870
|
+
|
|
871
|
+
// Support custom field names
|
|
872
|
+
const customNode = {
|
|
873
|
+
nodeId: 1,
|
|
874
|
+
subNodes: [{ nodeId: 2 }],
|
|
875
|
+
};
|
|
876
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
877
|
+
console.log(t.isValidTreeNode(customNode, fieldNames)) // true
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
**Difference from isSingleTreeData:**
|
|
881
|
+
- `isValidTreeNode` - Only checks the basic structure of a single node, doesn't recursively check child nodes (lightweight)
|
|
882
|
+
- `isSingleTreeData` - Recursively checks the entire tree structure, ensuring all child nodes are valid tree structures
|
|
883
|
+
|
|
884
|
+
### isTreeNodeWithCircularCheck
|
|
885
|
+
|
|
886
|
+
Check if a node is a valid tree node structure and detect circular references. Uses WeakSet to track visited nodes, returns false if circular references are found.
|
|
887
|
+
|
|
888
|
+
```javascript
|
|
889
|
+
// Valid tree node (no circular references)
|
|
890
|
+
const validNode = {
|
|
891
|
+
id: 1,
|
|
892
|
+
children: [
|
|
893
|
+
{ id: 2, children: [{ id: 3 }] },
|
|
894
|
+
],
|
|
895
|
+
};
|
|
896
|
+
console.log(t.isTreeNodeWithCircularCheck(validNode)) // true
|
|
897
|
+
|
|
898
|
+
// Detect circular references
|
|
899
|
+
const node1 = { id: 1, children: [] };
|
|
900
|
+
const node2 = { id: 2, children: [] };
|
|
901
|
+
node1.children.push(node2);
|
|
902
|
+
node2.children.push(node1); // circular reference
|
|
903
|
+
console.log(t.isTreeNodeWithCircularCheck(node1)) // false
|
|
904
|
+
|
|
905
|
+
// Detect self-reference
|
|
906
|
+
const selfRefNode = { id: 1, children: [] };
|
|
907
|
+
selfRefNode.children.push(selfRefNode); // self-reference
|
|
908
|
+
console.log(t.isTreeNodeWithCircularCheck(selfRefNode)) // false
|
|
909
|
+
|
|
910
|
+
// Support custom field names
|
|
911
|
+
const customNode = {
|
|
912
|
+
nodeId: 1,
|
|
913
|
+
subNodes: [{ nodeId: 2 }],
|
|
914
|
+
};
|
|
915
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
916
|
+
console.log(t.isTreeNodeWithCircularCheck(customNode, fieldNames)) // true
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
**Use Cases:**
|
|
920
|
+
- Check for circular references when receiving user input or external data
|
|
921
|
+
- Data validation to prevent infinite recursion
|
|
922
|
+
- Check if data structure is correct during debugging
|
|
923
|
+
|
|
924
|
+
### isSafeTreeDepth
|
|
925
|
+
|
|
926
|
+
Check if the depth of tree-structured data is safe (prevent recursion stack overflow). Returns false if the tree depth exceeds `maxDepth`.
|
|
927
|
+
|
|
928
|
+
```javascript
|
|
929
|
+
// Safe depth tree
|
|
930
|
+
const safeTree = [
|
|
931
|
+
{
|
|
932
|
+
id: 1,
|
|
933
|
+
children: [
|
|
934
|
+
{ id: 2, children: [{ id: 3 }] },
|
|
935
|
+
],
|
|
936
|
+
},
|
|
937
|
+
];
|
|
938
|
+
console.log(t.isSafeTreeDepth(safeTree, 10)) // true (depth is 3, less than 10)
|
|
939
|
+
|
|
940
|
+
// Depth exceeds maximum depth
|
|
941
|
+
const deepTree = [
|
|
942
|
+
{
|
|
943
|
+
id: 1,
|
|
944
|
+
children: [
|
|
945
|
+
{ id: 2, children: [{ id: 3 }] },
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
];
|
|
949
|
+
console.log(t.isSafeTreeDepth(deepTree, 2)) // false (depth is 3, exceeds 2)
|
|
950
|
+
|
|
951
|
+
// Empty tree is always safe
|
|
952
|
+
console.log(t.isSafeTreeDepth([], 10)) // true
|
|
953
|
+
|
|
954
|
+
// Single layer tree
|
|
955
|
+
const singleLayer = [{ id: 1 }, { id: 2 }];
|
|
956
|
+
console.log(t.isSafeTreeDepth(singleLayer, 1)) // true
|
|
957
|
+
|
|
958
|
+
// Support custom field names
|
|
959
|
+
const customTree = [
|
|
960
|
+
{
|
|
961
|
+
nodeId: 1,
|
|
962
|
+
subNodes: [
|
|
963
|
+
{ nodeId: 2, subNodes: [{ nodeId: 3 }] },
|
|
964
|
+
],
|
|
965
|
+
},
|
|
966
|
+
];
|
|
967
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
968
|
+
console.log(t.isSafeTreeDepth(customTree, 3, fieldNames)) // true
|
|
969
|
+
console.log(t.isSafeTreeDepth(customTree, 2, fieldNames)) // false
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Use Cases:**
|
|
973
|
+
- Check if depth is safe before processing large trees
|
|
974
|
+
- Prevent recursion call stack overflow
|
|
975
|
+
- Performance optimization, avoid processing trees that are too deep
|
|
976
|
+
|
|
977
|
+
## Custom Field Names
|
|
978
|
+
|
|
979
|
+
All methods support custom property names for children and id through the last parameter, passing in a configuration object:
|
|
980
|
+
|
|
981
|
+
```javascript
|
|
982
|
+
// Use default field names
|
|
983
|
+
const foundNode1 = t.findTree(treeData, (node) => node.id === 2)
|
|
984
|
+
|
|
985
|
+
// Use custom field names
|
|
986
|
+
const fieldNames = { children: 'subNodes', id: 'nodeId' };
|
|
987
|
+
const foundNode2 = t.findTree(customTreeData, (node) => node.nodeId === 2, fieldNames);
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
**Note:** All 30 functions support the `fieldNames` parameter to maintain API consistency. Even if the parameter has no effect in certain functions (such as `isEmptyTreeData`), it can still be passed to maintain consistent code style.
|
|
991
|
+
|
|
992
|
+
## Testing
|
|
993
|
+
|
|
994
|
+
### Run Tests
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
# Run all tests
|
|
998
|
+
npm test
|
|
999
|
+
|
|
1000
|
+
# Run tests and generate coverage report
|
|
1001
|
+
npm run test:coverage
|
|
1002
|
+
|
|
1003
|
+
# Run tests (once, don't watch for file changes)
|
|
1004
|
+
npm test -- --run
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
## Development
|
|
1008
|
+
|
|
1009
|
+
```bash
|
|
1010
|
+
# Install dependencies
|
|
1011
|
+
npm install
|
|
1012
|
+
|
|
1013
|
+
# Run tests
|
|
1014
|
+
npm test
|
|
1015
|
+
|
|
1016
|
+
# Build project
|
|
1017
|
+
npm run build
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
<div align="center">
|
|
1021
|
+
|
|
1022
|
+
If this project helps you, please give it a ⭐️
|
|
1023
|
+
|
|
1024
|
+
Made with by [knott11]
|
|
1025
|
+
|
|
1026
|
+
</div>
|
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# tree-processor
|
|
2
2
|
|
|
3
|
+
<div align="right">
|
|
4
|
+
|
|
5
|
+
[English](https://github.com/knott11/tree-processor/blob/main/README.en.md) | [中文](https://github.com/knott11/tree-processor/blob/main/README.md)
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
|
|
3
9
|
<div align="center">
|
|
4
10
|
|
|
5
11
|

|
|
6
12
|

|
|
7
13
|

|
|
8
|
-

|
|
9
14
|

|
|
10
|
-

|
|
11
16
|
|
|
12
17
|
一个轻量级的树结构数据处理工具库,使用 TypeScript 编写,支持 tree-shaking,每个格式打包体积约 **6-7 KB**(ESM: 6.39 KB,CJS: 6.64 KB,UMD: 6.68 KB)。
|
|
13
18
|
|
|
@@ -23,17 +28,13 @@
|
|
|
23
28
|
- [API 文档](#-api-文档)
|
|
24
29
|
- [遍历方法](#遍历方法)
|
|
25
30
|
- [查找方法](#查找方法)
|
|
31
|
+
- [访问方法](#访问方法)
|
|
26
32
|
- [修改方法](#修改方法)
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
33
|
+
- [查询方法](#查询方法)
|
|
34
|
+
- [验证方法](#验证方法)
|
|
29
35
|
- [自定义字段名](#自定义字段名)
|
|
30
36
|
- [测试](#测试)
|
|
31
37
|
- [开发](#开发)
|
|
32
|
-
- [技术栈](#技术栈)
|
|
33
|
-
- [贡献](#-贡献)
|
|
34
|
-
- [更新日志](#-更新日志)
|
|
35
|
-
- [许可证](#-许可证)
|
|
36
|
-
- [相关链接](#-相关链接)
|
|
37
38
|
|
|
38
39
|
## ✨ 特性
|
|
39
40
|
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
- **完整的 TypeScript 支持** - 提供完整的类型定义和智能提示,提升开发体验
|
|
43
44
|
- **灵活的自定义字段名** - 支持自定义 children 和 id 字段名,适配各种数据结构
|
|
44
45
|
- **零依赖** - 无任何外部依赖,开箱即用,无需担心依赖冲突
|
|
45
|
-
- **完善的测试覆盖** - 包含
|
|
46
|
+
- **完善的测试覆盖** - 包含 290 个测试用例,测试覆盖率达到 99%+(语句覆盖率 99.67%,分支覆盖率 99.32%,函数覆盖率 100%),覆盖基础功能、边界情况、异常处理、复杂场景等
|
|
46
47
|
- **丰富的 API** - 提供 30+ 个方法,包含类似数组的 API(map、filter、find、some、every、includes、at、indexOf 等),以及树结构特有的操作(获取父子节点、深度计算、数据验证等),涵盖遍历、查找、修改、判断等完整场景
|
|
47
48
|
|
|
48
49
|
**已支持的方法:** mapTree、forEachTree、filterTree、findTree、pushTree、unshiftTree、popTree、shiftTree、someTree、everyTree、includesTree、atTree、indexOfTree、atIndexOfTree、dedupTree、removeTree、getParentTree、getChildrenTree、getSiblingsTree、getNodeDepthMap、getNodeDepth、isLeafNode、isRootNode、isEmptyTreeData、isEmptySingleTreeData、isTreeData、isSingleTreeData、isValidTreeNode、isTreeNodeWithCircularCheck、isSafeTreeDepth。每个方法的最后一个参数可以自定义 children 和 id 的属性名。
|
|
@@ -123,16 +124,6 @@ import type { TreeNode, TreeData, FieldNames } from 'tree-processor'
|
|
|
123
124
|
const { mapTree, filterTree } = require('tree-processor')
|
|
124
125
|
```
|
|
125
126
|
|
|
126
|
-
**按需导入的优势:**
|
|
127
|
-
- ✅ 支持 tree-shaking,只打包使用的代码,减小打包体积
|
|
128
|
-
- ✅ 更好的代码提示和类型检查
|
|
129
|
-
- ✅ 更清晰的依赖关系
|
|
130
|
-
|
|
131
|
-
**关于类型导入:**
|
|
132
|
-
- TypeScript 会自动从函数签名推断类型,**大多数情况下不需要显式引入类型**
|
|
133
|
-
- 只有在需要显式声明变量类型时才需要引入类型(如 `const treeData: TreeData = [...]`)
|
|
134
|
-
- 使用 `import type` 导入类型不会增加运行时体积(类型在编译时会被移除)
|
|
135
|
-
|
|
136
127
|
### 示例数据
|
|
137
128
|
|
|
138
129
|
以下示例数据将用于后续所有方法的演示:
|
|
@@ -210,6 +201,10 @@ t.forEachTree(treeData, () => {
|
|
|
210
201
|
console.log(nodeCount) // 节点总数
|
|
211
202
|
```
|
|
212
203
|
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 查找方法
|
|
207
|
+
|
|
213
208
|
### filterTree
|
|
214
209
|
|
|
215
210
|
过滤树结构数据,返回满足条件的节点。
|
|
@@ -232,10 +227,6 @@ const leafNodes = t.filterTree(treeData, (node) => {
|
|
|
232
227
|
console.log(leafNodes) // 返回所有叶子节点
|
|
233
228
|
```
|
|
234
229
|
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## 查找方法
|
|
238
|
-
|
|
239
230
|
### findTree
|
|
240
231
|
|
|
241
232
|
查找树结构数据中满足条件的第一个节点。如果未找到,返回 null。
|
|
@@ -265,6 +256,38 @@ const hasNode = t.includesTree(treeData, nodeId)
|
|
|
265
256
|
console.log(hasNode) // true 表示包含该节点,false 表示不包含
|
|
266
257
|
```
|
|
267
258
|
|
|
259
|
+
### someTree
|
|
260
|
+
|
|
261
|
+
检查树结构数据中是否存在满足条件的节点。只要有一个节点满足条件就返回 true。
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
// 检查是否存在名称为 'node2' 的节点
|
|
265
|
+
const hasNode2 = t.someTree(treeData, node => node.name === 'node2')
|
|
266
|
+
console.log(hasNode2) // true
|
|
267
|
+
|
|
268
|
+
// 检查是否存在ID大于10的节点
|
|
269
|
+
const hasLargeId = t.someTree(treeData, node => node.id > 10)
|
|
270
|
+
console.log(hasLargeId) // false
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### everyTree
|
|
274
|
+
|
|
275
|
+
检查树结构数据中是否所有节点都满足条件。只有所有节点都满足条件才返回 true。
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
// 检查所有节点的ID是否都大于0
|
|
279
|
+
const allIdsPositive = t.everyTree(treeData, node => node.id > 0)
|
|
280
|
+
console.log(allIdsPositive) // true
|
|
281
|
+
|
|
282
|
+
// 检查所有节点是否都有 name 属性
|
|
283
|
+
const allHaveName = t.everyTree(treeData, node => node.name)
|
|
284
|
+
console.log(allHaveName) // 根据实际数据返回 true 或 false
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 访问方法
|
|
290
|
+
|
|
268
291
|
### atTree
|
|
269
292
|
|
|
270
293
|
根据父节点ID和子节点索引获取节点。支持负数索引,和数组的 at 方法一样。未找到返回 null。
|
|
@@ -403,35 +426,7 @@ console.log(uniqueByNameTree) // 返回根据 name 去重后的数据
|
|
|
403
426
|
|
|
404
427
|
---
|
|
405
428
|
|
|
406
|
-
##
|
|
407
|
-
|
|
408
|
-
### someTree
|
|
409
|
-
|
|
410
|
-
检查树结构数据中是否存在满足条件的节点。只要有一个节点满足条件就返回 true。
|
|
411
|
-
|
|
412
|
-
```javascript
|
|
413
|
-
// 检查是否存在名称为 'node2' 的节点
|
|
414
|
-
const hasNode2 = t.someTree(treeData, node => node.name === 'node2')
|
|
415
|
-
console.log(hasNode2) // true
|
|
416
|
-
|
|
417
|
-
// 检查是否存在ID大于10的节点
|
|
418
|
-
const hasLargeId = t.someTree(treeData, node => node.id > 10)
|
|
419
|
-
console.log(hasLargeId) // false
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### everyTree
|
|
423
|
-
|
|
424
|
-
检查树结构数据中是否所有节点都满足条件。只有所有节点都满足条件才返回 true。
|
|
425
|
-
|
|
426
|
-
```javascript
|
|
427
|
-
// 检查所有节点的ID是否都大于0
|
|
428
|
-
const allIdsPositive = t.everyTree(treeData, node => node.id > 0)
|
|
429
|
-
console.log(allIdsPositive) // true
|
|
430
|
-
|
|
431
|
-
// 检查所有节点是否都有 name 属性
|
|
432
|
-
const allHaveName = t.everyTree(treeData, node => node.name)
|
|
433
|
-
console.log(allHaveName) // 根据实际数据返回 true 或 false
|
|
434
|
-
```
|
|
429
|
+
## 查询方法
|
|
435
430
|
|
|
436
431
|
### getParentTree
|
|
437
432
|
|
|
@@ -522,10 +517,6 @@ const customSiblings = t.getSiblingsTree(customTree, 2, fieldNames)
|
|
|
522
517
|
console.log(customSiblings) // 返回兄弟节点数组(包括自己)
|
|
523
518
|
```
|
|
524
519
|
|
|
525
|
-
---
|
|
526
|
-
|
|
527
|
-
## 工具方法
|
|
528
|
-
|
|
529
520
|
### getNodeDepthMap
|
|
530
521
|
|
|
531
522
|
返回一个字典,键代表节点的 id,值代表该节点在数据的第几层。深度从1开始,根节点深度为1。
|
|
@@ -584,6 +575,10 @@ console.log(depth) // 2
|
|
|
584
575
|
- `getNodeDepthMap` - 批量获取所有节点的深度(一次性计算所有节点)
|
|
585
576
|
- `getNodeDepth` - 只获取单个节点的深度(只计算目标节点,效率更高)
|
|
586
577
|
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## 验证方法
|
|
581
|
+
|
|
587
582
|
### isLeafNode
|
|
588
583
|
|
|
589
584
|
检查节点是否是叶子节点(没有子节点)。轻量级方法,只检查节点本身,不遍历树。
|
|
@@ -1024,43 +1019,10 @@ npm test
|
|
|
1024
1019
|
npm run build
|
|
1025
1020
|
```
|
|
1026
1021
|
|
|
1027
|
-
## 技术栈
|
|
1028
|
-
|
|
1029
|
-
- **Rollup** - 模块打包工具
|
|
1030
|
-
- **Vitest** - 单元测试框架
|
|
1031
|
-
- **Terser** - JavaScript 压缩工具
|
|
1032
|
-
- **TypeScript** - 类型支持
|
|
1033
|
-
|
|
1034
|
-
## 🤝 贡献
|
|
1035
|
-
|
|
1036
|
-
欢迎贡献!如果你有任何想法或发现问题,请:
|
|
1037
|
-
|
|
1038
|
-
1. Fork 本仓库
|
|
1039
|
-
2. 创建你的特性分支 (`git checkout -b feature/AmazingFeature`)
|
|
1040
|
-
3. 提交你的更改 (`git commit -m 'Add some AmazingFeature'`)
|
|
1041
|
-
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
|
1042
|
-
5. 开启一个 Pull Request
|
|
1043
|
-
|
|
1044
|
-
## 📝 更新日志
|
|
1045
|
-
|
|
1046
|
-
查看 [CHANGELOG.md](./CHANGELOG.md) 了解详细的版本更新记录。
|
|
1047
|
-
|
|
1048
|
-
## 📄 许可证
|
|
1049
|
-
|
|
1050
|
-
本项目采用 [MIT](./LICENSE) 许可证。
|
|
1051
|
-
|
|
1052
|
-
## 🔗 相关链接
|
|
1053
|
-
|
|
1054
|
-
- [GitHub 仓库](https://github.com/knott11/tree-processor)
|
|
1055
|
-
- [npm 包](https://www.npmjs.com/package/tree-processor)
|
|
1056
|
-
- [问题反馈](https://github.com/knott11/tree-processor/issues)
|
|
1057
|
-
|
|
1058
|
-
---
|
|
1059
|
-
|
|
1060
1022
|
<div align="center">
|
|
1061
1023
|
|
|
1062
1024
|
如果这个项目对你有帮助,请给它一个 ⭐️
|
|
1063
1025
|
|
|
1064
|
-
Made with
|
|
1026
|
+
Made with by [knott11]
|
|
1065
1027
|
|
|
1066
1028
|
</div>
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAaD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAK3C,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC;AASlC,wBAAgB,OAAO,CACrB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,GAAG,EACjC,UAAU,GAAE,UAAgC,GAC3C,GAAG,EAAE,CAeP;AASD,wBAAgB,UAAU,CACxB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,EACpD,UAAU,GAAE,UAAgC,GAC3C,QAAQ,CAiBV;AASD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,EACxC,UAAU,GAAE,UAAgC,GAC3C,QAAQ,GAAG,IAAI,CAcjB;AAUD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,QAAQ,EACd,cAAc,EAAE,GAAG,EACnB,OAAO,EAAE,QAAQ,EACjB,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAqBT;AAUD,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,cAAc,EAAE,GAAG,EACnB,OAAO,EAAE,QAAQ,EACjB,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAqBT;AASD,wBAAgB,OAAO,CACrB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,GAAG,EACX,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAsBT;AASD,wBAAgB,SAAS,CACvB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,GAAG,EACX,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAsBT;AASD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,EACrC,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAaT;AASD,wBAAgB,SAAS,CACvB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,EACrC,UAAU,GAAE,UAAgC,GAC3C,OAAO,CAaT;AAUD,wBAAgB,MAAM,CACpB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,GAAG,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,UAAgC,GAC3C,QAAQ,GAAG,IAAI,CAqCjB;AASD,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,GAAG,EACb,UAAU,GAAE,UAAgC,GAC3C,MAAM,EAAE,GAAG,IAAI,CAsBjB;AASD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,GAAE,UAAgC,GAC3C,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/stats.html
CHANGED
|
@@ -4929,7 +4929,7 @@ var drawChart = (function (exports) {
|
|
|
4929
4929
|
</script>
|
|
4930
4930
|
<script>
|
|
4931
4931
|
/*<!--*/
|
|
4932
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"tree-processor.
|
|
4932
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"tree-processor.umd.js","children":[{"name":"src/index.ts","uid":"975942c2-1"}]}],"isRoot":true},"nodeParts":{"975942c2-1":{"renderedLength":17081,"gzipLength":2155,"brotliLength":1901,"metaUid":"975942c2-0"}},"nodeMetas":{"975942c2-0":{"id":"\\src\\index.ts","moduleParts":{"tree-processor.umd.js":"975942c2-1"},"imported":[],"importedBy":[],"isEntry":true}},"env":{"rollup":"4.57.1"},"options":{"gzip":true,"brotli":true,"sourcemap":false}};
|
|
4933
4933
|
|
|
4934
4934
|
const run = () => {
|
|
4935
4935
|
const width = window.innerWidth;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tree-processor",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "A lightweight TypeScript library for processing tree-structured data with comprehensive methods (map, filter, find, push, pop, remove, getParent, includes, etc.), supporting tree-shaking and custom field names",
|
|
5
5
|
"main": "dist/tree-processor.cjs.js",
|
|
6
6
|
"module": "dist/tree-processor.esm.js",
|
|
@@ -46,12 +46,6 @@
|
|
|
46
46
|
"deduplicate",
|
|
47
47
|
"menu",
|
|
48
48
|
"navigation",
|
|
49
|
-
"file-tree",
|
|
50
|
-
"directory",
|
|
51
|
-
"category",
|
|
52
|
-
"comment",
|
|
53
|
-
"permission",
|
|
54
|
-
"tree-select",
|
|
55
49
|
"tree-processor"
|
|
56
50
|
],
|
|
57
51
|
"author": "",
|