如何将平级待办事项数据转换为支持多级嵌套的层级JSON对象?
如何将平级待办事项数据转换为支持多级嵌套的层级JSON对象?
你好!你的思路其实已经找对方向了——先提取根节点再匹配子节点,但当前实现只能支持两级嵌套。如果要处理任意深度的层级结构,我们可以优化成递归或者用映射表的方式,既灵活又能保证性能,下面给你两种实用的方案,适配Next.js项目的最佳实践:
方案1:映射表优化版(高性能首选)
这种方式先把所有数据用ID做一个快速映射表,后续查找父节点/子节点都是O(1)的时间复杂度,整体时间复杂度O(n),适合处理大量待办数据的场景:
const data = [ {id: 2, Name:"Task 1.1", ParentID: 1}, {id: 5, Name:"Task 2", ParentID: null}, {id: 6, Name:"Task 2.1", ParentID: 5}, {id: 1, Name:"Task 1", ParentID: null}, {id: 3, Name:"Task 1.2", ParentID: 1}, {id: 4, Name:"Task 1.3", ParentID: 1}, {id: 7, Name:"Task 2.2", ParentID: 5}, {id: 8, Name:"Task 2.3", ParentID: 5}, {id: 9, Name:"Task 3", ParentID: null}, // 可以加个深层节点测试:{id:10, Name:"Task 1.1.1", ParentID:2} ]; function buildHierarchy(data) { // 第一步:创建ID到节点的映射,给每个节点初始化subtasks数组 const taskMap = new Map(); data.forEach(task => { taskMap.set(task.id, {...task, subtasks: []}); }); const result = []; // 第二步:遍历所有节点,把非根节点挂载到对应父节点的subtasks里 data.forEach(task => { if (task.ParentID === null) { // 根节点直接加入最终结果数组 result.push(taskMap.get(task.id)); } else { // 找到当前节点的父节点,把自己加入父节点的subtasks const parentTask = taskMap.get(task.ParentID); if (parentTask) { parentTask.subtasks.push(taskMap.get(task.id)); } } }); return result; } const result = buildHierarchy(data); console.log(JSON.stringify(result, null, 2));
方案2:递归查找版(代码简洁直观)
如果你的待办数据量不大,想要更简洁的代码,可以用递归函数直接给每个节点查找子节点,逻辑非常直观:
const data = [/* 同上数据 */]; function buildHierarchy(data, parentId = null) { // 过滤出当前父ID下的所有子节点,再递归给每个子节点找它的子节点 return data .filter(task => task.ParentID === parentId) .map(task => ({ ...task, subtasks: buildHierarchy(data, task.id) })); } const result = buildHierarchy(data); console.log(JSON.stringify(result, null, 2));
两种方案怎么选?
- 映射表方案:性能更优,适合数据量大的场景,比如有成百上千条待办的情况。
- 递归查找方案:代码更短更容易理解,但每次递归都要过滤整个数组,时间复杂度是O(n²),数据量小的时候用起来特别顺手。
Next.js项目里的最佳实践
- 数据处理时机:尽量在服务端或者请求回调里处理层级转换,比如在
getServerSideProps、getStaticProps,或者SWR/React Query的fetcher函数里处理,不要在组件渲染时重复执行,避免不必要的性能损耗。 - 缓存处理后的数据:如果数据不会频繁变化,用
useMemo缓存处理后的层级数据,防止组件重渲染时重复计算:import { useMemo } from 'react'; // 假设rawData是从接口获取的平级数据 const hierarchicalData = useMemo(() => buildHierarchy(rawData), [rawData]); - 类型安全(TypeScript项目):可以给待办事项定义接口,让代码更健壮:
interface Todo { id: number; Name: string; ParentID: number | null; } interface HierarchicalTodo extends Todo { subtasks: HierarchicalTodo[]; }
用上面的方案处理你给出的示例数据,都会完美生成你想要的层级结构,而且后续不管嵌套多少层,都能自动处理~
备注:内容来源于stack exchange,提问作者paipai
相关产品推荐
相关产品推荐

