tree-processor 0.10.0 → 0.11.1

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 CHANGED
@@ -9,12 +9,13 @@
9
9
  <div align="center">
10
10
 
11
11
  ![version](https://img.shields.io/npm/v/tree-processor?style=flat-square&label=version)
12
- ![npm downloads (2 months)](https://img.shields.io/badge/downloads-1.7K%2F2mo-brightgreen?style=flat-square)
13
- ![bundle size](https://img.shields.io/badge/bundle-8.4KB-blue?style=flat-square)
12
+ ![npm downloads (2 months)](https://img.shields.io/badge/downloads-1.8K%2F2mo-brightgreen?style=flat-square)
13
+ ![bundle size](https://img.shields.io/badge/bundle-15KB-blue?style=flat-square)
14
+ ![performance](https://img.shields.io/badge/performance-%3C%200.03ms-brightgreen?style=flat-square)
14
15
  ![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)
15
- ![coverage](https://img.shields.io/badge/coverage-99%25-brightgreen?style=flat-square)
16
+ ![coverage](https://img.shields.io/badge/coverage-97%25-green?style=flat-square)
16
17
 
17
- A lightweight tree-structured data processing utility library written in TypeScript, supporting tree-shaking, with each format bundle size approximately **8.2-8.5 KB** (ESM: 8.24 KB, CJS: 8.51 KB, UMD: 8.52 KB).
18
+ A lightweight tree-structured data processing utility library written in TypeScript, providing 50+ APIs including traversal, search, modification, conversion, query, analysis, and validation.
18
19
 
19
20
 
20
21
  </div>
@@ -26,28 +27,26 @@ A lightweight tree-structured data processing utility library written in TypeScr
26
27
  - [Installation](#-installation)
27
28
  - [Quick Start](#-quick-start)
28
29
  - [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
- - [Conversion Methods](#conversion-methods)
34
- - [Query Methods](#query-methods)
35
- - [Validation Methods](#validation-methods)
30
+ - [Traversal Operation Methods](#traversal-operation-methods)
31
+ - [Conditional Search Methods](#conditional-search-methods)
32
+ - [Index Access Methods](#index-access-methods)
33
+ - [Node Operation Methods](#node-operation-methods)
34
+ - [Format Conversion Methods](#format-conversion-methods)
35
+ - [Clone and Copy Methods](#clone-and-copy-methods)
36
+ - [Relationship Query Methods](#relationship-query-methods)
37
+ - [Data Validation Methods](#data-validation-methods)
38
+ - [Statistical Analysis Methods](#statistical-analysis-methods)
36
39
  - [Custom Field Names](#custom-field-names)
37
40
  - [Testing](#testing)
38
41
  - [Development](#development)
39
42
 
40
43
  ## ✨ Features
41
44
 
42
- - **Lightweight** - Each format bundle size is only 8.2-8.5 KB (ESM: 8.24 KB, CJS: 8.51 KB, UMD: 8.52 KB), minimal impact on project size
43
- - **Tree-shaking Support** - Supports on-demand imports, only bundles the code you actually use, further reducing bundle size
44
- - **Full TypeScript Support** - Provides complete type definitions and IntelliSense, improving development experience
45
- - **Flexible Custom Field Names** - Supports custom children and id field names, adapting to various data structures
46
- - **Zero Dependencies** - No external dependencies, ready to use out of the box, no need to worry about dependency conflicts
47
- - **Comprehensive Test Coverage** - Contains 328 test cases with 99%+ test coverage (99% statement coverage, 98.41% branch coverage, 100% function coverage, 98.99% line coverage), covering basic functionality, edge cases, error handling, and complex scenarios
48
- - **Rich API** - Provides 32+ 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, format conversion, etc.), covering complete scenarios for traversal, search, modification, conversion, and validation
49
-
50
- **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, convertToArrayTree, convertBackTree, convertToMapTree, convertToLevelArrayTree, convertToObjectTree. The last parameter of each method can customize the property names for children and id.
45
+ - **Multiple Format Support** - Provides ESM, CJS, UMD formats, bundle size only 14.9-15.2 KB, supports Tree-shaking, on-demand imports
46
+ - **Zero Dependencies** - No external dependencies, ready to use
47
+ - **High Performance** - Average execution time < 0.03ms on medium-sized trees (~120 nodes)
48
+ - **Complete Functionality** - 50+ APIs covering traversal, search, modification, conversion, query, analysis, and validation
49
+ - **Comprehensive Testing** - 447 test cases with 99%+ coverage
51
50
 
52
51
  ### 💡 Use Cases
53
52
 
@@ -153,7 +152,9 @@ const treeData = [
153
152
 
154
153
  ---
155
154
 
156
- ## Traversal Methods
155
+ ## Traversal Operation Methods
156
+
157
+ Methods for traversing tree-structured data and performing operations on each node.
157
158
 
158
159
  ### mapTree
159
160
 
@@ -202,7 +203,9 @@ console.log(nodeCount) // Total number of nodes
202
203
 
203
204
  ---
204
205
 
205
- ## Search Methods
206
+ ## Conditional Search Methods
207
+
208
+ Methods for finding nodes by conditions or predicate functions.
206
209
 
207
210
  ### filterTree
208
211
 
@@ -285,7 +288,9 @@ console.log(allHaveName) // Returns true or false based on actual data
285
288
 
286
289
  ---
287
290
 
288
- ## Access Methods
291
+ ## Index Access Methods
292
+
293
+ Methods for accessing nodes by position index or index path.
289
294
 
290
295
  ### atTree
291
296
 
@@ -345,7 +350,9 @@ console.log(invalidPath) // null
345
350
 
346
351
  ---
347
352
 
348
- ## Modification Methods
353
+ ## Node Operation Methods
354
+
355
+ Methods for adding, removing, and modifying nodes in tree structures.
349
356
 
350
357
  ### pushTree
351
358
 
@@ -375,26 +382,30 @@ console.log(treeData) // New node has been added to the beginning of the childre
375
382
 
376
383
  ### popTree
377
384
 
378
- 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.
385
+ Remove the last child node under the specified node. Returns the removed node, or null if the node doesn't exist or has no children.
379
386
 
380
387
  ```javascript
381
388
  // Remove the last child node under the node with ID 1
382
389
  const removedNode = t.popTree(treeData, 1)
383
- console.log(removedNode) // Returns the removed node object, or false
390
+ console.log(removedNode) // Returns the removed node object, or null
384
391
 
385
392
  // Try to remove from a non-existent node
386
393
  const popFailed = t.popTree(treeData, 999)
387
- console.log(popFailed) // false
394
+ console.log(popFailed) // null
388
395
  ```
389
396
 
390
397
  ### shiftTree
391
398
 
392
- 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.
399
+ Remove the first child node under the specified node. Returns the removed node, or null if the node doesn't exist or has no children.
393
400
 
394
401
  ```javascript
395
402
  // Remove the first child node under the node with ID 1
396
403
  const shiftedNode = t.shiftTree(treeData, 1)
397
- console.log(shiftedNode) // Returns the removed node object, or false
404
+ console.log(shiftedNode) // Returns the removed node object, or null
405
+
406
+ // Try to remove from a non-existent node
407
+ const shiftFailed = t.shiftTree(treeData, 999)
408
+ console.log(shiftFailed) // null
398
409
  ```
399
410
 
400
411
  ### removeTree
@@ -409,6 +420,90 @@ console.log(removeSuccess) // true means successful removal, false means node no
409
420
  console.log(treeData) // Tree structure after removal
410
421
  ```
411
422
 
423
+ ### concatTree
424
+
425
+ Concatenate multiple tree-structured data arrays, returns a new concatenated tree (deep cloned).
426
+
427
+ ```javascript
428
+ const tree1 = [
429
+ { id: 1, name: 'node1' },
430
+ { id: 2, name: 'node2' }
431
+ ]
432
+ const tree2 = [
433
+ { id: 3, name: 'node3' }
434
+ ]
435
+
436
+ // Concatenate multiple trees
437
+ const result = t.concatTree(tree1, tree2)
438
+ console.log(result) // [{ id: 1, name: 'node1' }, { id: 2, name: 'node2' }, { id: 3, name: 'node3' }]
439
+ ```
440
+
441
+ **Parameters:**
442
+ - `...trees`: Multiple tree-structured data arrays (variadic arguments)
443
+
444
+ **Notes:**
445
+ - All trees are deep cloned, won't modify the original trees
446
+ - Supports concatenating any number of tree structures
447
+
448
+ ### sortTree
449
+
450
+ Sort tree-structured data, recursively sorts all levels.
451
+
452
+ ```javascript
453
+ const tree = [
454
+ { id: 3, name: 'node3' },
455
+ { id: 1, name: 'node1' },
456
+ { id: 2, name: 'node2' }
457
+ ]
458
+
459
+ // Sort by id
460
+ const sorted = t.sortTree(tree, (a, b) => a.id - b.id)
461
+ console.log(sorted)
462
+ // [{ id: 1, name: 'node1' }, { id: 2, name: 'node2' }, { id: 3, name: 'node3' }]
463
+ ```
464
+
465
+ **Parameters:**
466
+ - `tree`: Tree-structured data
467
+ - `compareFn`: Comparison function, same as `Array.sort`'s `compareFn` (optional)
468
+ - `fieldNames`: Custom field name configuration (optional)
469
+
470
+ **Notes:**
471
+ - Recursively sorts nodes at all levels
472
+ - Returns a new sorted tree (deep cloned), won't modify the original tree
473
+ - Uses default sorting if `compareFn` is not provided
474
+
475
+ ### sliceTree
476
+
477
+ Slice the root nodes of tree-structured data (similar to array's `slice`).
478
+
479
+ ```javascript
480
+ const tree = [
481
+ { id: 1, name: 'node1' },
482
+ { id: 2, name: 'node2' },
483
+ { id: 3, name: 'node3' }
484
+ ]
485
+
486
+ // Slice: get nodes from index 1 to 3
487
+ const sliced = t.sliceTree(tree, 1, 3)
488
+ console.log(sliced) // [{ id: 2, name: 'node2' }, { id: 3, name: 'node3' }]
489
+
490
+ // Supports negative indices
491
+ const lastTwo = t.sliceTree(tree, -2)
492
+ console.log(lastTwo) // [{ id: 2, name: 'node2' }, { id: 3, name: 'node3' }]
493
+ ```
494
+
495
+ **Parameters:**
496
+ - `tree`: Tree-structured data
497
+ - `start`: Start index (inclusive), optional
498
+ - `end`: End index (exclusive), optional
499
+ - `fieldNames`: Custom field name configuration (optional)
500
+
501
+ **Notes:**
502
+ - Only slices root nodes, doesn't recursively process children
503
+ - Returns a new sliced tree (deep cloned), won't modify the original tree
504
+ - Supports negative indices (calculated from the end)
505
+ - Child node structures are completely preserved
506
+
412
507
  ### dedupTree
413
508
 
414
509
  Tree-structured object array deduplication method that removes duplicate nodes based on the specified key. Keeps the first occurrence of the node. Supports single field, multiple fields combined deduplication, and custom functions.
@@ -447,13 +542,16 @@ const uniqueByComplex = t.dedupTree(treeData, (node) => `${node.id}-${node.type}
447
542
  ```
448
543
 
449
544
  **Notes:**
450
- - If the key value is `undefined` or `null`, the node will not be deduplicated (all will be kept)
545
+ - If the dedupKey value is `undefined` or `null`, the node will not be deduplicated (all will be kept)
451
546
  - Multiple fields combined deduplication uses the combination of field values to determine duplicates
452
547
  - Recursively processes all levels of child nodes
548
+ - **Performance Optimization**: Multiple fields combined deduplication has been optimized, using efficient delimiter concatenation instead of JSON.stringify for better performance
453
549
 
454
550
  ---
455
551
 
456
- ## Conversion Methods
552
+ ## Format Conversion Methods
553
+
554
+ Methods for converting between different data formats (array, Map, object, etc.).
457
555
 
458
556
  ### convertToArrayTree
459
557
 
@@ -727,7 +825,169 @@ console.log(treeFromRecord) // Correctly converted to tree structure
727
825
 
728
826
  ---
729
827
 
730
- ## Query Methods
828
+ ## Clone and Copy Methods
829
+
830
+ Methods for copying tree-structured data (deep copy, shallow copy, subtree copy, etc.).
831
+
832
+ ### cloneTree
833
+
834
+ Deep clone tree-structured data, returns a completely independent copy without modifying the original tree.
835
+
836
+ ```javascript
837
+ const original = [
838
+ { id: 1, name: 'node1', children: [{ id: 2, name: 'node2' }] }
839
+ ]
840
+
841
+ // Deep clone
842
+ const cloned = t.cloneTree(original)
843
+
844
+ // Modifying the cloned tree won't affect the original
845
+ cloned[0].name = 'modified'
846
+ console.log(original[0].name) // 'node1'
847
+ console.log(cloned[0].name) // 'modified'
848
+ ```
849
+
850
+ **Parameters:**
851
+ - `tree`: Tree-structured data
852
+ - `fieldNames`: Custom field name configuration (optional)
853
+
854
+ **Notes:**
855
+ - Recursively deep clones all levels of nodes and children
856
+ - Returns a tree completely independent from the original, modifications won't affect each other
857
+ - Supports custom field name configuration
858
+
859
+ ### shallowCloneTree
860
+
861
+ Shallow clone tree-structured data (only copies the first level, children share references). Better performance than deep clone, suitable for scenarios where only the top-level structure needs to be copied.
862
+
863
+ ```javascript
864
+ const original = [
865
+ { id: 1, name: 'node1', children: [{ id: 2, name: 'node2' }] }
866
+ ]
867
+
868
+ // Shallow clone
869
+ const cloned = t.shallowCloneTree(original)
870
+
871
+ // Modifying the first level won't affect the original
872
+ cloned[0].name = 'modified'
873
+ console.log(original[0].name) // 'node1'
874
+
875
+ // But children share references, modifying children will affect the original
876
+ cloned[0].children[0].name = 'changed'
877
+ console.log(original[0].children[0].name) // 'changed'
878
+ ```
879
+
880
+ **Parameters:**
881
+ - `tree`: Tree-structured data
882
+ - `fieldNames`: Custom field name configuration (optional)
883
+
884
+ **Notes:**
885
+ - Only copies the first level of nodes, children maintain shared references
886
+ - Better performance than deep clone, suitable for scenarios where only top-level independence is needed
887
+ - Modifying children will affect the original tree
888
+
889
+ ### cloneSubtree
890
+
891
+ Clone a subtree starting from the specified node. Returns a deep copy containing the target node and all its children. Supports finding nodes by any field.
892
+
893
+ ```javascript
894
+ const tree = [
895
+ {
896
+ id: 1,
897
+ name: 'root',
898
+ children: [
899
+ { id: 2, name: 'sub1', children: [{ id: 4, name: 'sub1-1' }] },
900
+ { id: 3, name: 'sub2' }
901
+ ]
902
+ }
903
+ ]
904
+
905
+ // Find by id field
906
+ const subtree1 = t.cloneSubtree(tree, { id: 2 })
907
+ console.log(subtree1)
908
+ // [{ id: 2, name: 'sub1', children: [{ id: 4, name: 'sub1-1' }] }]
909
+
910
+ // Find by name field
911
+ const subtree2 = t.cloneSubtree(tree, { name: 'sub1' })
912
+ console.log(subtree2)
913
+ // [{ id: 2, name: 'sub1', children: [{ id: 4, name: 'sub1-1' }] }]
914
+
915
+ // Find by other field (e.g., code)
916
+ const treeWithCode = [
917
+ {
918
+ id: 1,
919
+ code: 'A001',
920
+ children: [
921
+ { id: 2, code: 'B001', children: [{ id: 4, code: 'C001' }] }
922
+ ]
923
+ }
924
+ ]
925
+ const subtree3 = t.cloneSubtree(treeWithCode, { code: 'B001' })
926
+ console.log(subtree3)
927
+ // [{ id: 2, code: 'B001', children: [{ id: 4, code: 'C001' }] }]
928
+
929
+ // Support custom children field name
930
+ const customTree = [
931
+ { nodeId: 1, subNodes: [{ nodeId: 2 }] }
932
+ ]
933
+ const subtree4 = t.cloneSubtree(customTree, { nodeId: 2 }, { children: 'subNodes', id: 'nodeId' })
934
+ console.log(subtree4)
935
+ // [{ nodeId: 2 }]
936
+
937
+ // Modifying the cloned subtree won't affect the original
938
+ subtree1[0].name = 'modified'
939
+ console.log(tree[0].children[0].name) // 'sub1'
940
+ ```
941
+
942
+ **Parameters:**
943
+ - `tree`: Tree-structured data
944
+ - `target`: Target node object, e.g., `{ id: 1 }` or `{ name: 'sub1' }` or `{ code: 'B001' }`, object must contain only one field
945
+ - `fieldNames`: Custom field name configuration (optional, used to customize `children` field name, search field is determined by the key name in the `target` object)
946
+
947
+ **Notes:**
948
+ - Returns a subtree containing the target node (deep cloned)
949
+ - Returns an empty array if the target node is not found
950
+ - Recursively deep clones all child nodes
951
+ - Must pass an object, search field is determined by the object's key name (e.g., `{ id: 1 }` searches by `id` field, `{ name: 'xxx' }` searches by `name` field)
952
+ - `fieldNames` parameter is used to customize `children` field name, defining `id` has no effect
953
+
954
+ ### cloneWithTransform
955
+
956
+ Clone tree-structured data and apply a transform function to each node. Suitable for modifying node data while cloning.
957
+
958
+ ```javascript
959
+ const tree = [
960
+ { id: 1, name: 'node1', children: [{ id: 2, name: 'node2' }] }
961
+ ]
962
+
963
+ // Clone and add label field
964
+ const cloned = t.cloneWithTransform(tree, (node) => ({
965
+ ...node,
966
+ label: node.name,
967
+ processed: true
968
+ }))
969
+
970
+ console.log(cloned[0].label) // 'node1'
971
+ console.log(cloned[0].processed) // true
972
+ console.log(cloned[0].children[0].label) // 'node2'
973
+ console.log(tree[0].label) // undefined (original tree not modified)
974
+ ```
975
+
976
+ **Parameters:**
977
+ - `tree`: Tree-structured data
978
+ - `transform`: Transform function that receives a node and returns the transformed node
979
+ - `fieldNames`: Custom field name configuration (optional)
980
+
981
+ **Notes:**
982
+ - Recursively transforms all levels of nodes
983
+ - Returns the transformed tree (deep cloned), won't modify the original tree
984
+ - The transform function should return a new node object
985
+
986
+ ---
987
+
988
+ ## Relationship Query Methods
989
+
990
+ Methods for getting relationship information between nodes (parent-child relationships, sibling relationships, depth, etc.).
731
991
 
732
992
  ### getParentTree
733
993
 
@@ -878,7 +1138,9 @@ console.log(depth) // 2
878
1138
 
879
1139
  ---
880
1140
 
881
- ## Validation Methods
1141
+ ## Data Validation Methods
1142
+
1143
+ Methods for validating the validity of tree-structured data and node types.
882
1144
 
883
1145
  ### isLeafNode
884
1146
 
@@ -930,6 +1192,8 @@ console.log(t.isLeafNode(customNode, fieldNames)) // true
930
1192
 
931
1193
  Check if a node is a root node (has no parent). Root nodes are top-level nodes in the tree-structured data array.
932
1194
 
1195
+ **Performance Optimization**: Optimized to use a single traversal, avoiding duplicate tree traversals.
1196
+
933
1197
  ```javascript
934
1198
  // Check root node
935
1199
  const treeData = [
@@ -1277,6 +1541,435 @@ console.log(t.isSafeTreeDepth(customTree, 2, fieldNames)) // false
1277
1541
  - Prevent recursion call stack overflow
1278
1542
  - Performance optimization, avoid processing trees that are too deep
1279
1543
 
1544
+ ---
1545
+
1546
+ ## Statistical Analysis Methods
1547
+
1548
+ Methods for statistical analysis of tree-structured data.
1549
+
1550
+ ### reduceTree
1551
+
1552
+ Reduce tree-structured data, traverses all nodes and accumulates results.
1553
+
1554
+ ```javascript
1555
+ const tree = [
1556
+ { id: 1, value: 10 },
1557
+ { id: 2, value: 20, children: [{ id: 3, value: 30 }] }
1558
+ ]
1559
+
1560
+ // Calculate the sum of all node values
1561
+ const sum = t.reduceTree(tree, (acc, node) => acc + (node.value || 0), 0)
1562
+ console.log(sum) // 60
1563
+
1564
+ // Collect all node IDs
1565
+ const ids = t.reduceTree(tree, (ids, node) => {
1566
+ ids.push(node.id)
1567
+ return ids
1568
+ }, [])
1569
+ console.log(ids) // [1, 2, 3]
1570
+ ```
1571
+
1572
+ **Parameters:**
1573
+ - `tree`: Tree-structured data
1574
+ - `reducer`: Reduction function that receives accumulator and current node, returns new accumulator
1575
+ - `initialValue`: Initial value
1576
+ - `fieldNames`: Custom field name configuration (optional)
1577
+
1578
+ **Notes:**
1579
+ - Traverses all nodes in depth-first order
1580
+ - Can be used to implement various aggregation operations
1581
+
1582
+ ---
1583
+
1584
+ ### aggregateTree
1585
+
1586
+ Aggregate tree-structured data by groups, supports multiple aggregation operations (sum, average, max, min, count).
1587
+
1588
+ ```javascript
1589
+ const tree = [
1590
+ { id: 1, category: 'A', value: 10, score: 80 },
1591
+ { id: 2, category: 'A', value: 20, score: 90 },
1592
+ { id: 3, category: 'B', value: 30, score: 70, children: [{ id: 4, category: 'B', value: 40, score: 85 }] }
1593
+ ]
1594
+
1595
+ // Aggregate by category
1596
+ const result = t.aggregateTree(tree, {
1597
+ groupBy: node => node.category,
1598
+ aggregations: {
1599
+ totalValue: { operation: 'sum', field: 'value' },
1600
+ avgScore: { operation: 'avg', field: 'score' },
1601
+ maxValue: { operation: 'max', field: 'value' },
1602
+ count: { operation: 'count' }
1603
+ }
1604
+ })
1605
+
1606
+ console.log(result)
1607
+ // {
1608
+ // 'A': { totalValue: 30, avgScore: 85, maxValue: 20, count: 2 },
1609
+ // 'B': { totalValue: 70, avgScore: 77.5, maxValue: 40, count: 2 }
1610
+ // }
1611
+ ```
1612
+
1613
+ **Parameters:**
1614
+ - `tree`: Tree-structured data
1615
+ - `options`: Aggregation options
1616
+ - `groupBy`: Grouping function that receives a node and returns a group key
1617
+ - `aggregations`: Aggregation configuration object, key is result field name, value is aggregation config
1618
+ - `operation`: Aggregation operation type ('sum' | 'avg' | 'max' | 'min' | 'count')
1619
+ - `field`: Field name to aggregate (not needed for count operation)
1620
+ - `fieldNames`: Custom field name configuration (optional)
1621
+
1622
+ **Notes:**
1623
+ - Supports multiple aggregation operations simultaneously
1624
+ - Recursively processes all levels of nodes
1625
+ - Count operation counts node quantity, doesn't need field parameter
1626
+
1627
+ ---
1628
+
1629
+ ### groupTree
1630
+
1631
+ Group tree-structured data by field, returns node arrays grouped by field value.
1632
+
1633
+ ```javascript
1634
+ const tree = [
1635
+ { id: 1, category: 'A' },
1636
+ { id: 2, category: 'A' },
1637
+ { id: 3, category: 'B', children: [{ id: 4, category: 'B' }] }
1638
+ ]
1639
+
1640
+ // Group by category field
1641
+ const grouped = t.groupTree(tree, 'category')
1642
+ console.log(grouped)
1643
+ // {
1644
+ // 'A': [{ id: 1, category: 'A' }, { id: 2, category: 'A' }],
1645
+ // 'B': [{ id: 3, category: 'B' }, { id: 4, category: 'B' }]
1646
+ // }
1647
+ ```
1648
+
1649
+ **Parameters:**
1650
+ - `tree`: Tree-structured data
1651
+ - `field`: Grouping field name
1652
+ - `fieldNames`: Custom field name configuration (optional)
1653
+
1654
+ **Notes:**
1655
+ - Returns references to original nodes, not deep copies
1656
+ - Recursively processes all levels of nodes
1657
+
1658
+ ---
1659
+
1660
+ ### groupByTree
1661
+
1662
+ Group tree-structured data by condition, uses custom function to determine group key.
1663
+
1664
+ ```javascript
1665
+ const tree = [
1666
+ { id: 1, value: 10 },
1667
+ { id: 2, value: 20 },
1668
+ { id: 3, value: 10, children: [{ id: 4, value: 30 }] }
1669
+ ]
1670
+
1671
+ // Group by whether value is >= 20
1672
+ const grouped = t.groupByTree(tree, node => node.value >= 20 ? 'high' : 'low')
1673
+ console.log(grouped)
1674
+ // {
1675
+ // 'low': [{ id: 1, value: 10 }, { id: 3, value: 10 }],
1676
+ // 'high': [{ id: 2, value: 20 }, { id: 4, value: 30 }]
1677
+ // }
1678
+ ```
1679
+
1680
+ **Parameters:**
1681
+ - `tree`: Tree-structured data
1682
+ - `groupFn`: Grouping function that receives a node and returns a group key
1683
+ - `fieldNames`: Custom field name configuration (optional)
1684
+
1685
+ **Notes:**
1686
+ - Group keys are converted to strings
1687
+ - Returns references to original nodes, not deep copies
1688
+
1689
+ ---
1690
+
1691
+ ### sumTree
1692
+
1693
+ Calculate the sum of a field in tree-structured data.
1694
+
1695
+ ```javascript
1696
+ const tree = [
1697
+ { id: 1, value: 10 },
1698
+ { id: 2, value: 20, children: [{ id: 3, value: 30 }] }
1699
+ ]
1700
+
1701
+ // Calculate sum of value field
1702
+ const total = t.sumTree(tree, 'value')
1703
+ console.log(total) // 60
1704
+ ```
1705
+
1706
+ **Parameters:**
1707
+ - `tree`: Tree-structured data
1708
+ - `field`: Field name
1709
+ - `fieldNames`: Custom field name configuration (optional)
1710
+
1711
+ **Notes:**
1712
+ - Missing or null/undefined values are treated as 0
1713
+ - Recursively processes all levels of nodes
1714
+
1715
+ ---
1716
+
1717
+ ### avgTree
1718
+
1719
+ Calculate the average value of a field in tree-structured data.
1720
+
1721
+ ```javascript
1722
+ const tree = [
1723
+ { id: 1, value: 10 },
1724
+ { id: 2, value: 20 },
1725
+ { id: 3, value: 30 }
1726
+ ]
1727
+
1728
+ // Calculate average of value field
1729
+ const average = t.avgTree(tree, 'value')
1730
+ console.log(average) // 20
1731
+ ```
1732
+
1733
+ **Parameters:**
1734
+ - `tree`: Tree-structured data
1735
+ - `field`: Field name
1736
+ - `fieldNames`: Custom field name configuration (optional)
1737
+
1738
+ **Notes:**
1739
+ - Ignores null and undefined values
1740
+ - Returns 0 if all values are null/undefined
1741
+
1742
+ ---
1743
+
1744
+ ### maxTree
1745
+
1746
+ Get the maximum value of a field in tree-structured data.
1747
+
1748
+ ```javascript
1749
+ const tree = [
1750
+ { id: 1, value: 10 },
1751
+ { id: 2, value: 30 },
1752
+ { id: 3, value: 20 }
1753
+ ]
1754
+
1755
+ // Get maximum value of value field
1756
+ const max = t.maxTree(tree, 'value')
1757
+ console.log(max) // 30
1758
+ ```
1759
+
1760
+ **Parameters:**
1761
+ - `tree`: Tree-structured data
1762
+ - `field`: Field name
1763
+ - `fieldNames`: Custom field name configuration (optional)
1764
+
1765
+ **Notes:**
1766
+ - Only processes numeric values
1767
+ - Returns null if tree is empty or has no valid values
1768
+
1769
+ ---
1770
+
1771
+ ### minTree
1772
+
1773
+ Get the minimum value of a field in tree-structured data.
1774
+
1775
+ ```javascript
1776
+ const tree = [
1777
+ { id: 1, value: 30 },
1778
+ { id: 2, value: 10 },
1779
+ { id: 3, value: 20 }
1780
+ ]
1781
+
1782
+ // Get minimum value of value field
1783
+ const min = t.minTree(tree, 'value')
1784
+ console.log(min) // 10
1785
+ ```
1786
+
1787
+ **Parameters:**
1788
+ - `tree`: Tree-structured data
1789
+ - `field`: Field name
1790
+ - `fieldNames`: Custom field name configuration (optional)
1791
+
1792
+ **Notes:**
1793
+ - Only processes numeric values
1794
+ - Returns null if tree is empty or has no valid values
1795
+
1796
+ ---
1797
+
1798
+ ### countTree
1799
+
1800
+ Count the number of nodes in tree-structured data that meet a condition.
1801
+
1802
+ ```javascript
1803
+ const tree = [
1804
+ { id: 1, value: 10 },
1805
+ { id: 2, value: 20 },
1806
+ { id: 3, value: 10, children: [{ id: 4, value: 30 }] }
1807
+ ]
1808
+
1809
+ // Count all nodes
1810
+ const total = t.countTree(tree)
1811
+ console.log(total) // 4
1812
+
1813
+ // Count nodes that meet condition
1814
+ const count = t.countTree(tree, node => node.value === 10)
1815
+ console.log(count) // 2
1816
+ ```
1817
+
1818
+ **Parameters:**
1819
+ - `tree`: Tree-structured data
1820
+ - `conditionFn`: Condition function (optional), if not provided counts all nodes
1821
+ - `fieldNames`: Custom field name configuration (optional)
1822
+
1823
+ **Notes:**
1824
+ - Counts all nodes if condition function is not provided
1825
+ - Recursively processes all levels of nodes
1826
+
1827
+ ---
1828
+
1829
+ ### getTreeStats
1830
+
1831
+ Get comprehensive statistics of tree-structured data.
1832
+
1833
+ ```javascript
1834
+ const tree = [
1835
+ { id: 1, children: [{ id: 2 }, { id: 3, children: [{ id: 4 }] }] }
1836
+ ]
1837
+
1838
+ // Get statistics
1839
+ const stats = t.getTreeStats(tree)
1840
+ console.log(stats)
1841
+ // {
1842
+ // totalNodes: 4, // Total number of nodes
1843
+ // leafNodes: 2, // Number of leaf nodes
1844
+ // maxDepth: 3, // Maximum depth
1845
+ // minDepth: 1, // Minimum depth
1846
+ // avgDepth: 2, // Average depth
1847
+ // levels: 3 // Number of levels (equals maxDepth)
1848
+ // }
1849
+ ```
1850
+
1851
+ **Parameters:**
1852
+ - `tree`: Tree-structured data
1853
+ - `fieldNames`: Custom field name configuration (optional)
1854
+
1855
+ **Notes:**
1856
+ - Returns complete statistics object
1857
+ - Empty tree returns statistics with all values as 0
1858
+
1859
+ ---
1860
+
1861
+ ### analyzeTree
1862
+
1863
+ Comprehensively analyze tree-structured data, providing detailed statistics, distribution, balance analysis, and more.
1864
+
1865
+ ```javascript
1866
+ const tree = [
1867
+ { id: 1, children: [{ id: 2 }, { id: 3, children: [{ id: 4 }] }] }
1868
+ ]
1869
+
1870
+ // Comprehensive tree structure analysis
1871
+ const analysis = t.analyzeTree(tree)
1872
+ console.log(analysis)
1873
+ // {
1874
+ // // Basic Statistics
1875
+ // totalNodes: 4, // Total number of nodes
1876
+ // leafNodes: 2, // Number of leaf nodes
1877
+ // internalNodes: 2, // Number of internal nodes
1878
+ // maxDepth: 3, // Maximum depth
1879
+ // minDepth: 1, // Minimum depth
1880
+ // avgDepth: 2, // Average depth
1881
+ // levels: 3, // Number of levels
1882
+ //
1883
+ // // Level Analysis
1884
+ // byLevel: { 0: 1, 1: 2, 2: 1 }, // Node count by level
1885
+ // maxWidth: 2, // Maximum width (max nodes in a single level)
1886
+ // avgWidth: 1.33, // Average width
1887
+ // widthByLevel: { 0: 1, 1: 2, 2: 1 }, // Width per level
1888
+ //
1889
+ // // Branching Factor Analysis
1890
+ // avgBranchingFactor: 1.5, // Average branching factor (avg children per node)
1891
+ // maxBranchingFactor: 2, // Maximum branching factor
1892
+ // minBranchingFactor: 1, // Minimum branching factor
1893
+ // branchingFactorDistribution: { 1: 1, 2: 1 }, // Branching factor distribution
1894
+ //
1895
+ // // Depth Distribution
1896
+ // depthDistribution: { 1: 1, 2: 2, 3: 1 }, // Node count by depth
1897
+ //
1898
+ // // Balance Analysis
1899
+ // depthVariance: 0.5, // Depth variance (smaller = more balanced)
1900
+ // isBalanced: true, // Whether tree is balanced
1901
+ // balanceRatio: 0.33, // Balance ratio (minDepth/maxDepth)
1902
+ //
1903
+ // // Path Analysis
1904
+ // avgPathLength: 2.25, // Average path length
1905
+ // maxPathLength: 3, // Maximum path length
1906
+ // minPathLength: 1, // Minimum path length
1907
+ //
1908
+ // // Leaf Node Analysis
1909
+ // leafNodeRatio: 0.5, // Leaf node ratio
1910
+ // leafNodesByLevel: { 2: 1, 3: 1 } // Leaf nodes per level
1911
+ // }
1912
+ ```
1913
+
1914
+ **Parameters:**
1915
+ - `tree`: Tree-structured data
1916
+ - `options`: Analysis options (optional), can specify which statistics to calculate, defaults to calculating all statistics
1917
+ - `includeBasic`: Whether to include basic statistics (totalNodes, leafNodes, internalNodes, maxDepth, minDepth, avgDepth, levels), default `true`
1918
+ - `includeLevelAnalysis`: Whether to include level analysis (byLevel, maxWidth, avgWidth, widthByLevel), default `true`
1919
+ - `includeBranchingFactor`: Whether to include branching factor analysis (avgBranchingFactor, maxBranchingFactor, minBranchingFactor, branchingFactorDistribution), default `true`
1920
+ - `includeDepthDistribution`: Whether to include depth distribution (depthDistribution), default `true`
1921
+ - `includeBalanceAnalysis`: Whether to include balance analysis (depthVariance, isBalanced, balanceRatio), default `true`
1922
+ - `includePathAnalysis`: Whether to include path analysis (avgPathLength, maxPathLength, minPathLength), default `true`
1923
+ - `includeLeafAnalysis`: Whether to include leaf node analysis (leafNodeRatio, leafNodesByLevel), default `true`
1924
+ - `fieldNames`: Custom field name configuration (optional)
1925
+
1926
+ ```javascript
1927
+ // Calculate only basic statistics and branching factor (performance optimization)
1928
+ const quickAnalysis = t.analyzeTree(tree, {
1929
+ includeBasic: true,
1930
+ includeBranchingFactor: true,
1931
+ includeLevelAnalysis: false,
1932
+ includeDepthDistribution: false,
1933
+ includeBalanceAnalysis: false,
1934
+ includePathAnalysis: false,
1935
+ includeLeafAnalysis: false,
1936
+ })
1937
+ console.log(quickAnalysis.totalNodes) // 4
1938
+ console.log(quickAnalysis.maxBranchingFactor) // 2
1939
+ console.log(quickAnalysis.byLevel) // {} (not calculated)
1940
+
1941
+ // Calculate only balance analysis
1942
+ const balanceAnalysis = t.analyzeTree(tree, {
1943
+ includeBasic: true,
1944
+ includeBalanceAnalysis: true,
1945
+ includeLevelAnalysis: false,
1946
+ includeBranchingFactor: false,
1947
+ includeDepthDistribution: false,
1948
+ includePathAnalysis: false,
1949
+ includeLeafAnalysis: false,
1950
+ })
1951
+ console.log(balanceAnalysis.isBalanced) // true/false
1952
+ console.log(balanceAnalysis.depthVariance) // 0.5
1953
+ ```
1954
+
1955
+ **Analysis information includes:**
1956
+
1957
+ 1. **Basic Statistics**: Total nodes, leaf nodes, internal nodes, depth information, etc.
1958
+ 2. **Level Analysis**: Nodes per level, maximum width, average width, etc.
1959
+ 3. **Branching Factor Analysis**: Average/max/min branching factor, branching factor distribution, etc.
1960
+ 4. **Depth Distribution**: Number of nodes at each depth
1961
+ 5. **Balance Analysis**: Depth variance, whether balanced, balance ratio, etc.
1962
+ 6. **Path Analysis**: Average/max/min path length
1963
+ 7. **Leaf Node Analysis**: Leaf node ratio, leaf nodes per level
1964
+
1965
+ **Notes:**
1966
+ - Provides comprehensive tree structure analysis, suitable for performance optimization, structure evaluation, etc.
1967
+ - `isBalanced` is determined based on depth variance and depth range. A tree is considered balanced if depth variance < 2 and depth range ≤ 2
1968
+ - `balanceRatio` closer to 1 indicates a more balanced tree
1969
+ - **Performance Optimization**: Use the `options` parameter to calculate only the needed statistics, which can significantly improve performance for large tree structures
1970
+
1971
+ ---
1972
+
1280
1973
  ## Custom Field Names
1281
1974
 
1282
1975
  All methods support custom property names for children and id through the last parameter, passing in a configuration object:
@@ -1297,16 +1990,16 @@ const foundNode2 = t.findTree(customTreeData, (node) => node.nodeId === 2, field
1297
1990
  ### Run Tests
1298
1991
 
1299
1992
  ```bash
1300
- # Run all tests (automatically build then test source + bundled files, 656 test cases)
1993
+ # Run all tests (automatically build then test source + bundled files, 712 test cases)
1301
1994
  npm test
1302
1995
 
1303
1996
  # Run all tests (once, don't watch for file changes)
1304
1997
  npm test -- --run
1305
1998
 
1306
- # Test source code only (328 test cases)
1999
+ # Test source code only (447 test cases)
1307
2000
  npm run test:src
1308
2001
 
1309
- # Test bundled files only (328 test cases, requires npm run build first)
2002
+ # Test bundled files only (447 test cases, requires npm run build first)
1310
2003
  npm run test:dist
1311
2004
 
1312
2005
  # Run tests and generate coverage report