tree-processor 0.8.2 → 0.9.0
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 +293 -12
- package/README.md +293 -14
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/stats.html +1 -1
- package/dist/tree-processor.cjs.js +130 -95
- package/dist/tree-processor.esm.js +72 -37
- package/dist/tree-processor.umd.js +70 -35
- package/package.json +11 -4
package/README.en.md
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
11
11
|

|
|
12
|
-
](https://img.shields.io/badge/downloads-1.3K-brightgreen?style=flat-square&label=2mo)
|
|
13
|
+

|
|
14
14
|

|
|
15
15
|

|
|
16
16
|
|
|
17
|
-
A lightweight tree-structured data processing utility library written in TypeScript, supporting tree-shaking, with each format bundle size approximately **
|
|
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
18
|
|
|
19
19
|
|
|
20
20
|
</div>
|
|
@@ -30,6 +30,7 @@ A lightweight tree-structured data processing utility library written in TypeScr
|
|
|
30
30
|
- [Search Methods](#search-methods)
|
|
31
31
|
- [Access Methods](#access-methods)
|
|
32
32
|
- [Modification Methods](#modification-methods)
|
|
33
|
+
- [Conversion Methods](#conversion-methods)
|
|
33
34
|
- [Query Methods](#query-methods)
|
|
34
35
|
- [Validation Methods](#validation-methods)
|
|
35
36
|
- [Custom Field Names](#custom-field-names)
|
|
@@ -38,15 +39,15 @@ A lightweight tree-structured data processing utility library written in TypeScr
|
|
|
38
39
|
|
|
39
40
|
## ✨ Features
|
|
40
41
|
|
|
41
|
-
- **Lightweight** - Each format bundle size is only
|
|
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
|
|
42
43
|
- **Tree-shaking Support** - Supports on-demand imports, only bundles the code you actually use, further reducing bundle size
|
|
43
44
|
- **Full TypeScript Support** - Provides complete type definitions and IntelliSense, improving development experience
|
|
44
45
|
- **Flexible Custom Field Names** - Supports custom children and id field names, adapting to various data structures
|
|
45
46
|
- **Zero Dependencies** - No external dependencies, ready to use out of the box, no need to worry about dependency conflicts
|
|
46
|
-
- **Comprehensive Test Coverage** - Contains
|
|
47
|
-
- **Rich API** - Provides
|
|
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
|
|
48
49
|
|
|
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
|
+
**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.
|
|
50
51
|
|
|
51
52
|
### 💡 Use Cases
|
|
52
53
|
|
|
@@ -424,6 +425,280 @@ console.log(uniqueByNameTree) // Returns data deduplicated by name
|
|
|
424
425
|
|
|
425
426
|
---
|
|
426
427
|
|
|
428
|
+
## Conversion Methods
|
|
429
|
+
|
|
430
|
+
### convertToArrayTree
|
|
431
|
+
|
|
432
|
+
Flatten tree-structured data into an array. The returned array contains nodes without the `children` field.
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
// Flatten tree structure into an array
|
|
436
|
+
const array = t.convertToArrayTree(treeData)
|
|
437
|
+
console.log(array)
|
|
438
|
+
// [
|
|
439
|
+
// { id: 1, name: 'node1' },
|
|
440
|
+
// { id: 2, name: 'node2' },
|
|
441
|
+
// { id: 4, name: 'node4' },
|
|
442
|
+
// { id: 5, name: 'node5' },
|
|
443
|
+
// { id: 3, name: 'node3' },
|
|
444
|
+
// { id: 6, name: 'node6' }
|
|
445
|
+
// ]
|
|
446
|
+
|
|
447
|
+
// Note: Returned nodes do not contain the children field
|
|
448
|
+
array.forEach(node => {
|
|
449
|
+
console.log(node.children) // undefined
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
// Support custom field names
|
|
453
|
+
const customTree = [
|
|
454
|
+
{
|
|
455
|
+
nodeId: 1,
|
|
456
|
+
name: 'node1',
|
|
457
|
+
subNodes: [
|
|
458
|
+
{ nodeId: 2, name: 'node2' }
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
const customArray = t.convertToArrayTree(customTree, {
|
|
463
|
+
children: 'subNodes',
|
|
464
|
+
id: 'nodeId'
|
|
465
|
+
})
|
|
466
|
+
console.log(customArray) // Flattened array without subNodes field
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### convertToMapTree
|
|
470
|
+
|
|
471
|
+
Convert tree-structured data to a Map, where the key is the node ID and the value is the node object (without the `children` field). Suitable for scenarios requiring fast node lookup by ID.
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
// Convert tree structure to Map
|
|
475
|
+
const map = t.convertToMapTree(treeData)
|
|
476
|
+
console.log(map instanceof Map) // true
|
|
477
|
+
console.log(map.size) // 6
|
|
478
|
+
|
|
479
|
+
// Quickly find node by ID
|
|
480
|
+
const node = map.get(2)
|
|
481
|
+
console.log(node) // { id: 2, name: 'node2' }
|
|
482
|
+
console.log(node.children) // undefined (does not contain children field)
|
|
483
|
+
|
|
484
|
+
// Support custom field names
|
|
485
|
+
const customTree = [
|
|
486
|
+
{
|
|
487
|
+
nodeId: 1,
|
|
488
|
+
name: 'node1',
|
|
489
|
+
subNodes: [
|
|
490
|
+
{ nodeId: 2, name: 'node2' }
|
|
491
|
+
]
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
const customMap = t.convertToMapTree(customTree, {
|
|
495
|
+
children: 'subNodes',
|
|
496
|
+
id: 'nodeId'
|
|
497
|
+
})
|
|
498
|
+
console.log(customMap.get(1)) // { nodeId: 1, name: 'node1' }
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### convertToLevelArrayTree
|
|
502
|
+
|
|
503
|
+
Convert tree-structured data to a level array (two-dimensional array), grouped by depth. The outer array is indexed by depth, and the inner array contains all nodes at that depth.
|
|
504
|
+
|
|
505
|
+
```javascript
|
|
506
|
+
// Convert tree structure to level array
|
|
507
|
+
const levelArray = t.convertToLevelArrayTree(treeData)
|
|
508
|
+
console.log(levelArray)
|
|
509
|
+
// [
|
|
510
|
+
// [{ id: 1, name: 'node1' }], // Level 0
|
|
511
|
+
// [{ id: 2, name: 'node2' }, { id: 3, name: 'node3' }], // Level 1
|
|
512
|
+
// [{ id: 4, name: 'node4' }, { id: 5, name: 'node5' }, { id: 6, name: 'node6' }] // Level 2
|
|
513
|
+
// ]
|
|
514
|
+
|
|
515
|
+
// Traverse each level
|
|
516
|
+
levelArray.forEach((level, depth) => {
|
|
517
|
+
console.log(`Depth ${depth}:`, level)
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
// Note: Returned nodes do not contain children field
|
|
521
|
+
levelArray[0][0].children // undefined
|
|
522
|
+
|
|
523
|
+
// Support custom field names
|
|
524
|
+
const customTree = [
|
|
525
|
+
{
|
|
526
|
+
nodeId: 1,
|
|
527
|
+
name: 'node1',
|
|
528
|
+
subNodes: [
|
|
529
|
+
{ nodeId: 2, name: 'node2' }
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
const customLevelArray = t.convertToLevelArrayTree(customTree, {
|
|
534
|
+
children: 'subNodes',
|
|
535
|
+
id: 'nodeId'
|
|
536
|
+
})
|
|
537
|
+
console.log(customLevelArray) // Array grouped by level
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### convertToObjectTree
|
|
541
|
+
|
|
542
|
+
Convert single-root tree-structured data to an object. If the tree has only one root node, returns that node object; otherwise returns `null`.
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
// Convert single-root tree to object
|
|
546
|
+
const singleRootTree = [
|
|
547
|
+
{
|
|
548
|
+
id: 1,
|
|
549
|
+
name: 'node1',
|
|
550
|
+
value: 100,
|
|
551
|
+
children: [
|
|
552
|
+
{ id: 2, name: 'node2' }
|
|
553
|
+
]
|
|
554
|
+
}
|
|
555
|
+
]
|
|
556
|
+
const rootNode = t.convertToObjectTree(singleRootTree)
|
|
557
|
+
console.log(rootNode)
|
|
558
|
+
// {
|
|
559
|
+
// id: 1,
|
|
560
|
+
// name: 'node1',
|
|
561
|
+
// value: 100,
|
|
562
|
+
// children: [{ id: 2, name: 'node2' }]
|
|
563
|
+
// }
|
|
564
|
+
|
|
565
|
+
// Multiple root nodes return null
|
|
566
|
+
const multiRootTree = [
|
|
567
|
+
{ id: 1, name: 'node1' },
|
|
568
|
+
{ id: 2, name: 'node2' }
|
|
569
|
+
]
|
|
570
|
+
const result = t.convertToObjectTree(multiRootTree)
|
|
571
|
+
console.log(result) // null
|
|
572
|
+
|
|
573
|
+
// Empty tree returns null
|
|
574
|
+
const emptyTree = []
|
|
575
|
+
const emptyResult = t.convertToObjectTree(emptyTree)
|
|
576
|
+
console.log(emptyResult) // null
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### convertBackTree
|
|
580
|
+
|
|
581
|
+
Convert various data structures to tree-structured data. Supports array, Map, Record (object) and other formats. Each element in the array needs to contain `id` and `parentId` fields.
|
|
582
|
+
|
|
583
|
+
```javascript
|
|
584
|
+
// Convert flat array to tree structure
|
|
585
|
+
const array = [
|
|
586
|
+
{ id: 1, name: 'node1', parentId: null },
|
|
587
|
+
{ id: 2, name: 'node2', parentId: 1 },
|
|
588
|
+
{ id: 3, name: 'node3', parentId: 1 },
|
|
589
|
+
{ id: 4, name: 'node4', parentId: 2 },
|
|
590
|
+
{ id: 5, name: 'node5', parentId: 2 },
|
|
591
|
+
{ id: 6, name: 'node6', parentId: 3 }
|
|
592
|
+
]
|
|
593
|
+
const tree = t.convertBackTree(array)
|
|
594
|
+
console.log(tree)
|
|
595
|
+
// [
|
|
596
|
+
// {
|
|
597
|
+
// id: 1,
|
|
598
|
+
// name: 'node1',
|
|
599
|
+
// children: [
|
|
600
|
+
// {
|
|
601
|
+
// id: 2,
|
|
602
|
+
// name: 'node2',
|
|
603
|
+
// children: [
|
|
604
|
+
// { id: 4, name: 'node4', children: [] },
|
|
605
|
+
// { id: 5, name: 'node5', children: [] }
|
|
606
|
+
// ]
|
|
607
|
+
// },
|
|
608
|
+
// {
|
|
609
|
+
// id: 3,
|
|
610
|
+
// name: 'node3',
|
|
611
|
+
// children: [
|
|
612
|
+
// { id: 6, name: 'node6', children: [] }
|
|
613
|
+
// ]
|
|
614
|
+
// }
|
|
615
|
+
// ]
|
|
616
|
+
// }
|
|
617
|
+
// ]
|
|
618
|
+
|
|
619
|
+
// Custom root node parentId value
|
|
620
|
+
const arrayWithZero = [
|
|
621
|
+
{ id: 1, name: 'node1', parentId: 0 },
|
|
622
|
+
{ id: 2, name: 'node2', parentId: 1 }
|
|
623
|
+
]
|
|
624
|
+
const treeWithZero = t.convertBackTree(arrayWithZero, { rootParentId: 0 })
|
|
625
|
+
console.log(treeWithZero) // Correctly converted
|
|
626
|
+
|
|
627
|
+
// Custom parentId field name
|
|
628
|
+
const arrayWithPid = [
|
|
629
|
+
{ id: 1, name: 'node1', pid: null },
|
|
630
|
+
{ id: 2, name: 'node2', pid: 1 }
|
|
631
|
+
]
|
|
632
|
+
const treeWithPid = t.convertBackTree(arrayWithPid, { parentIdField: 'pid' })
|
|
633
|
+
console.log(treeWithPid) // Correctly converted
|
|
634
|
+
|
|
635
|
+
// Support custom field names
|
|
636
|
+
const customArray = [
|
|
637
|
+
{ nodeId: 1, name: 'node1', parentId: null },
|
|
638
|
+
{ nodeId: 2, name: 'node2', parentId: 1 }
|
|
639
|
+
]
|
|
640
|
+
const customTree = t.convertBackTree(customArray, {
|
|
641
|
+
fieldNames: { id: 'nodeId', children: 'subNodes' }
|
|
642
|
+
})
|
|
643
|
+
console.log(customTree)
|
|
644
|
+
// [
|
|
645
|
+
// {
|
|
646
|
+
// nodeId: 1,
|
|
647
|
+
// name: 'node1',
|
|
648
|
+
// subNodes: [
|
|
649
|
+
// { nodeId: 2, name: 'node2', subNodes: [] }
|
|
650
|
+
// ]
|
|
651
|
+
// }
|
|
652
|
+
// ]
|
|
653
|
+
|
|
654
|
+
// Handle multiple root nodes
|
|
655
|
+
const multiRootArray = [
|
|
656
|
+
{ id: 1, name: 'root1', parentId: null },
|
|
657
|
+
{ id: 2, name: 'root2', parentId: null },
|
|
658
|
+
{ id: 3, name: 'child1', parentId: 1 }
|
|
659
|
+
]
|
|
660
|
+
const multiRootTree = t.convertBackTree(multiRootArray)
|
|
661
|
+
console.log(multiRootTree) // Contains two root nodes
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**Parameter Description:**
|
|
665
|
+
- `data` - Supports multiple data formats:
|
|
666
|
+
- Array: Flat array, each element needs to contain `id` and `parentId` fields
|
|
667
|
+
- Map: key is node ID, value is node object
|
|
668
|
+
- Record (object): key is node ID, value is node object
|
|
669
|
+
- Single object: Single tree node object
|
|
670
|
+
- `options.rootParentId` - The parentId value for root nodes, defaults to `null`
|
|
671
|
+
- `options.parentIdField` - Parent node ID field name, defaults to `'parentId'`
|
|
672
|
+
- `options.fieldNames` - Custom field name configuration, supports custom `id` and `children` field names
|
|
673
|
+
|
|
674
|
+
**Notes:**
|
|
675
|
+
- If a node's `parentId` cannot find a corresponding parent node, that node will be treated as a root node
|
|
676
|
+
- Nodes without `id` will be skipped
|
|
677
|
+
- Nodes with `parentId` as `null`, `undefined`, or equal to `rootParentId` will be treated as root nodes
|
|
678
|
+
- When converting Map and Record formats, the key will be set as the node's `id`
|
|
679
|
+
|
|
680
|
+
**Example: Support Map and Record formats**
|
|
681
|
+
|
|
682
|
+
```javascript
|
|
683
|
+
// Map format
|
|
684
|
+
const map = new Map([
|
|
685
|
+
[1, { name: 'node1', parentId: null }],
|
|
686
|
+
[2, { name: 'node2', parentId: 1 }]
|
|
687
|
+
])
|
|
688
|
+
const treeFromMap = t.convertBackTree(map)
|
|
689
|
+
console.log(treeFromMap) // Correctly converted to tree structure
|
|
690
|
+
|
|
691
|
+
// Record format
|
|
692
|
+
const record = {
|
|
693
|
+
1: { name: 'node1', parentId: null },
|
|
694
|
+
2: { name: 'node2', parentId: 1 }
|
|
695
|
+
}
|
|
696
|
+
const treeFromRecord = t.convertBackTree(record)
|
|
697
|
+
console.log(treeFromRecord) // Correctly converted to tree structure
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
427
702
|
## Query Methods
|
|
428
703
|
|
|
429
704
|
### getParentTree
|
|
@@ -994,14 +1269,20 @@ const foundNode2 = t.findTree(customTreeData, (node) => node.nodeId === 2, field
|
|
|
994
1269
|
### Run Tests
|
|
995
1270
|
|
|
996
1271
|
```bash
|
|
997
|
-
# Run all tests
|
|
1272
|
+
# Run all tests (automatically build then test source + bundled files, 656 test cases)
|
|
998
1273
|
npm test
|
|
999
1274
|
|
|
1275
|
+
# Run all tests (once, don't watch for file changes)
|
|
1276
|
+
npm test -- --run
|
|
1277
|
+
|
|
1278
|
+
# Test source code only (328 test cases)
|
|
1279
|
+
npm run test:src
|
|
1280
|
+
|
|
1281
|
+
# Test bundled files only (328 test cases, requires npm run build first)
|
|
1282
|
+
npm run test:dist
|
|
1283
|
+
|
|
1000
1284
|
# Run tests and generate coverage report
|
|
1001
1285
|
npm run test:coverage
|
|
1002
|
-
|
|
1003
|
-
# Run tests (once, don't watch for file changes)
|
|
1004
|
-
npm test -- --run
|
|
1005
1286
|
```
|
|
1006
1287
|
|
|
1007
1288
|
## Development
|
|
@@ -1013,7 +1294,7 @@ npm install
|
|
|
1013
1294
|
# Run tests
|
|
1014
1295
|
npm test
|
|
1015
1296
|
|
|
1016
|
-
# Build project
|
|
1297
|
+
# Build project (delete dist directory first, then rebuild)
|
|
1017
1298
|
npm run build
|
|
1018
1299
|
```
|
|
1019
1300
|
|