1+ /**
2+ * 将列表数据转换为树形数据 (迭代方式)
3+ * 1. 首先创建一个 map 对象,用于通过 id 快速查找节点。
4+ * 2. 遍历列表,将每个节点添加到 map 中,并初始化 children 数组。
5+ * 3. 再次遍历列表,将每个节点添加到其父节点的 children 数组中。
6+ * 4. 最后返回根节点数组。
7+ *
8+ * 复杂度分析:
9+ * 1. 时间复杂度:O(n),其中 n 是列表的长度。
10+ * 2. 空间复杂度:O(n),其中 n 是列表的长度。
11+ */
12+
13+ type ListItem = {
14+ [ key : string ] : any ;
15+ } ;
16+
17+ // 配置项类型
18+ //! 需要注意 childrenKey 的类型推导
19+ type Options < T extends ListItem , R extends ListItem , K extends string = "children" > = {
20+ /**
21+ * 节点标识字段
22+ * @default "id"
23+ */
24+ idKey ?: string ;
25+
26+ /**
27+ * 父节点标识字段
28+ * @default "parentId"
29+ */
30+ parentKey ?: string ;
31+
32+ /**
33+ * 子节点字段
34+ * @default "children"
35+ */
36+ childrenKey ?: K ;
37+
38+ /**
39+ * 转换节点函数, 默认copy当前节点
40+ * @param item 当前节点
41+ * @returns
42+ */
43+ transform ?: ( item : T ) => R ;
44+
45+ /**
46+ * 判断是否为根节点
47+ * @param item 当前节点
48+ * @returns
49+ */
50+ isRoot ?: ( item : T ) => boolean ;
51+ } ;
52+
53+ // 定义树节点类型,结合 transform 返回值和动态的 children 字段
54+ // 使用映射类型 [P in K] 来创建动态的 children 字段名
55+ type TreeNode < R extends ListItem , K extends string > = R & {
56+ [ P in K ] : TreeNode < R , K > [ ] ;
57+ } ;
58+
59+ /**
60+ * Converts a flat list of items into a hierarchical tree structure.
61+ * @param list List data
62+ * @param options Options
63+ * @returns tree data
64+ */
65+ function listToTree < T extends ListItem , R extends ListItem = T , K extends string = "children" > (
66+ list : T [ ] ,
67+ options : Options < T , R , K > = { } as Options < T , R , K >
68+ ) : TreeNode < R , K > [ ] {
69+ const {
70+ idKey = 'id' ,
71+ parentKey = 'parentId' ,
72+ childrenKey = 'children' as K , //默认值的类型断言
73+ transform = ( ( item : T ) => ( { ...item } as unknown as R ) ) , //! 注意需要类型断言
74+ isRoot = ( ( item : T ) => ! item [ parentKey ] ) ,
75+ } = options ;
76+
77+ if ( ! Array . isArray ( list ) ) {
78+ throw new TypeError ( 'Expected an array of items' ) ;
79+ }
80+
81+ if ( ! list . length ) {
82+ return [ ] ;
83+ }
84+
85+ if ( typeof transform !== 'function' ) {
86+ throw new TypeError ( 'Expected transform to be a function' ) ;
87+ }
88+
89+ if ( typeof isRoot !== 'function' ) {
90+ throw new TypeError ( 'Expected isRoot to be a function' ) ;
91+ }
92+
93+ const map : Record < string | number , TreeNode < R , K > > = { } ;
94+ const roots : TreeNode < R , K > [ ] = [ ] ;
95+
96+ // 建立映射关系,方便通过 id 快速查找
97+ for ( const node of list ) {
98+ map [ node [ idKey ] ] = {
99+ ...transform ( node ) ,
100+ [ childrenKey ] : [ ] ,
101+ } as TreeNode < R , K > ;
102+ }
103+
104+ // 构建树形结构
105+ for ( const node of list ) {
106+ const parentId = node [ parentKey ] ;
107+
108+ if ( isRoot ( node ) ) {
109+ roots . push ( map [ node [ idKey ] ] ) ;
110+ } else {
111+ const parent = map [ parentId ] ;
112+ if ( parent ) {
113+ parent [ childrenKey ] . push ( map [ node [ idKey ] ] ) ;
114+ }
115+ }
116+ }
117+
118+ return roots ;
119+ }
120+
121+ export default listToTree ;
0 commit comments