Skip to content

Instantly share code, notes, and snippets.

@zourdyzou
Created June 18, 2024 10:17
Show Gist options
  • Select an option

  • Save zourdyzou/438b6fde23420466da600fbc42bed70e to your computer and use it in GitHub Desktop.

Select an option

Save zourdyzou/438b6fde23420466da600fbc42bed70e to your computer and use it in GitHub Desktop.
import { useMemo, useState } from 'react';
/**
* A type that extends the generic type T to include a children property.
*/
type TreeNode<T> = T & { [key: string]: any; children?: TreeNode<T>[] };
/**
* Options for configuring the useTreeFormatData hook.
*
* @property {string} [childrenKey='children'] - The key to access children nodes in the data.
* @property {string} [parentIdKey='parentId'] - The key to access the parent ID in the data.
*/
interface UseTreeFormatDataOptions {
childrenKey?: string;
parentIdKey?: string;
}
/**
* A hook to format a flat data array into a tree structure.
*
* @template T - The type of the data elements.
* @param {T[]} data - The flat data array to be transformed into a tree structure.
* @param {UseTreeFormatDataOptions} options - Configuration options for the tree structure.
* @returns {TreeNode<T>[]} The data array transformed into a tree structure.
*/
export function useTreeFormatData<T>(
data: T[],
options: UseTreeFormatDataOptions = {},
): TreeNode<T>[] {
const { childrenKey = 'children', parentIdKey = 'parentId' } = options;
const [dataCopy] = useState(() => JSON.parse(JSON.stringify(data)) as T[]);
const formatTreeData = (data: T[]): TreeNode<T>[] => {
const tree: TreeNode<T>[] = [];
const listId = data.map((item) => (item as any).id);
data.forEach((item) => {
if (
(item as any)[parentIdKey] !== null &&
!listId.includes((item as any)[parentIdKey])
) {
(item as any)[parentIdKey] = null;
}
});
data.sort((a, b) =>
(a as any).name
.toLowerCase()
.localeCompare((b as any).name.toLowerCase()),
);
const dataWithChildren = data.map((item) => ({
...item,
[childrenKey]: [],
})) as TreeNode<T>[];
dataWithChildren.forEach((item) => {
if ((item as any)[parentIdKey] === null) {
tree.push(item);
} else {
const parent = dataWithChildren.find(
(d) => (d as any).id === (item as any)[parentIdKey],
);
if (parent) {
(parent[childrenKey] as TreeNode<T>[]).push(item);
}
}
});
return tree;
};
return useMemo(() => formatTreeData(dataCopy), [dataCopy]);
}
/**
* Usage Example:
*
* const groupData: GroupData[] = [
* { id: '1', name: 'Group 1', rootGroup: true, subGroups: [] },
* { id: '2', name: 'Group 2', parentGroupId: '1', subGroups: [] },
* { id: '3', name: 'Group 3', parentGroupId: '1', subGroups: [] },
* ];
*
* const options = { childrenKey: 'subGroups', parentIdKey: 'parentGroupId' };
*
* const tree = useTreeFormat<GroupData>(groupData, options);
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment