Skip to content

Commit

Permalink
feat: add toTree util
Browse files Browse the repository at this point in the history
  • Loading branch information
wangxingkang committed Aug 28, 2023
1 parent 0cc090e commit d3dd00c
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
"import": "./react/index.js",
"types": "./react/index.d.ts"
},
"./tree": {
"require": "./_cjs/tree/index.js",
"import": "./tree/index.js",
"types": "./tree/index.d.ts"
},
"./types": "./types/index.d.ts",
"./utils": {
"require": "./_cjs/utils/index.js",
Expand Down
1 change: 1 addition & 0 deletions src/tree/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { toTree } from './toTree';
59 changes: 59 additions & 0 deletions src/tree/toTree.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { toTree } from './toTree';

describe('toTree', function () {
it('should convert flat array to tree structure', function () {
const flatArray = [
{ id: '1', parentId: '' },
{ id: '2', parentId: '1' },
{ id: '3', parentId: '1' },
{ id: '4', parentId: '2' },
{ id: '5', parentId: '' },
];

expect(toTree(flatArray)).toEqual([
{
id: '1',
parentId: '',
children: [
{
id: '2',
parentId: '1',
children: [{ id: '4', parentId: '2', children: [] }],
},
{ id: '3', parentId: '1', children: [] },
],
},
{ id: '5', parentId: '', children: [] },
]);
});

it('should support custom rootId', () => {
const flatArray = [
{ id: 1, parentId: 'root', name: 'Root 1' },
{ id: 2, parentId: 1, name: 'Child 1.1' },
];

const tree = toTree(flatArray, { rootId: 'root' });

expect(tree).toHaveLength(1);
expect(tree[0].children).toHaveLength(1);
});

it('should support data formatting', () => {
const flatArray = [
{ id: 1, parentId: '', title: 'Root 1' },
{ id: 2, parentId: 1, title: 'Child 1.1' },
];

const format = (item: any) => ({
id: item.id,
parentId: item.parentId,
name: item.title,
});

const tree = toTree(flatArray, { format });

expect(tree[0].name).toBe('Root 1');
expect(tree[0].children[0].name).toBe('Child 1.1');
});
});
51 changes: 51 additions & 0 deletions src/tree/toTree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { ITreeListItem, ITreeKey, IChildrenKey, ITree } from './types';

interface IToTreeProps {
rootId?: ITreeKey;
/**
* 格式化数据为统一数据格式
* @param item
* @returns
*/
format?: (item: any) => ITreeListItem;
}

/**
* 将数组转换为树结构
* @param list
* @param opts
* @returns
*/
export function toTree<T extends IChildrenKey = 'children'>(
list: any[] = [],
opts: IToTreeProps = {},
): ITree<T> {
const { rootId = '', format } = opts;

if (!Array.isArray(list)) {
throw new TypeError('Expected an array but got an invalid argument');
}

const map = new Map<ITreeKey, ITreeListItem>();
const roots: any[] = [];

// 构建节点的映射关系
list.forEach((item) => {
const itemData = format ? format(item) : item;
map.set(itemData.id, { ...itemData, children: [] });
});

// 构建树结构
list.forEach((item) => {
const itemData = format ? format(item) : item;
const node = map.get(itemData.id);

if (itemData.parentId === rootId) {
roots.push(node);
} else if (map.get(itemData.parentId)) {
map.get(itemData.parentId)!.children.push(node);
}
});

return roots as ITree<T>;
}
17 changes: 17 additions & 0 deletions src/tree/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type ITreeKey = string | number;

export type IChildrenKey = ITreeKey;

export type ITree<T extends IChildrenKey = 'children'> = {
[key in T]?: ITree<T>[];
} & {
[key in ITreeKey]: any;
};

export interface ITreeListItem {
id: ITreeKey;
parentId: ITreeKey;
[key: string]: any;
}

const tree: ITree = [{ id: '', children: [] }];

0 comments on commit d3dd00c

Please sign in to comment.