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 CHANGED
@@ -9,12 +9,12 @@
9
9
  <div align="center">
10
10
 
11
11
  ![npm version](https://img.shields.io/npm/v/tree-processor?style=flat-square)
12
- ![npm downloads](https://img.shields.io/npm/dm/tree-processor?style=flat-square)
13
- ![bundle size](https://img.shields.io/badge/bundle-6.6KB-blue?style=flat-square)
12
+ ![npm downloads (2 months)](https://img.shields.io/badge/downloads-1.3K-brightgreen?style=flat-square&label=2mo)
13
+ ![bundle size](https://img.shields.io/badge/bundle-8.4KB-blue?style=flat-square)
14
14
  ![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)
15
15
  ![coverage](https://img.shields.io/badge/coverage-99%25-brightgreen?style=flat-square)
16
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).
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 6-7 KB (ESM: 6.39 KB, CJS: 6.64 KB, UMD: 6.68 KB), minimal impact on project size
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 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
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