如何在JS/TS中获取指定分类ID子节点并扁平化(禁用循环)
问题描述
给定如下嵌套数据:
const data = [ { name: "Car", id: "19", count: "20", depth: "1", children: [ { name: "Wheel", id: "22", count: "3", depth: "2", children: [ { name: "Engine", id: "101", count: "1", depth: "3", children: [ { name: "Engine and Brakes", id: "344", count: "1", depth: "4", children: [] } ] } ] } ] }, { name: "Bike", id: "3", count: "12", depth: "1", children: [ { name: "SpeedBike", id: "4", count: "12", depth: "2", children: [] } ] } ];
需要实现一个函数满足以下三个需求:
- 传入多个分类ID(如
['22', '3']):获取这些ID对应的所有子节点并扁平化,返回剔除depth和children的精简对象数组 - 未传入分类ID:默认返回所有父节点(depth=1)及其直接子节点的精简对象数组
- 传入的分类ID无子女节点:返回空数组
[]
要求:避免使用for、forEach、while循环,需适配任意多层嵌套的节点结构,支持JavaScript/TypeScript。
解决方案
1. 核心工具函数
扁平化节点的所有后代
用递归+flatMap实现,自动处理嵌套层级并返回精简后的节点数组:
const flattenDescendants = (node) => { return node.children.length === 0 ? [] : node.children.flatMap(child => [ { name: child.name, id: child.id, count: child.count }, ...flattenDescendants(child) ]); };
根据ID递归查找节点
用reduce遍历节点树,找到匹配ID的节点:
const findNodeById = (nodes, id) => { return nodes.reduce((found, node) => { if (found) return found; if (node.id === id) return node; return findNodeById(node.children, id); }, undefined); };
2. 主函数实现
整合工具函数,覆盖所有需求场景:
const getTargetNodes = (categoryIds) => { // 处理未传入ID的默认场景 if (!categoryIds || categoryIds.length === 0) { return data.flatMap(node => [ { name: node.name, id: node.id, count: node.count }, ...node.children.map(child => ({ name: child.name, id: child.id, count: child.count })) ]); } // 处理传入ID的场景 return categoryIds.flatMap(id => { const targetNode = findNodeById(data, id); return targetNode ? flattenDescendants(targetNode) : []; }); };
3. TypeScript类型优化(可选)
为了代码严谨性,可添加类型定义:
interface Node { name: string; id: string; count: string; depth: string; children: Node[]; } interface SimplifiedNode { name: string; id: string; count: string; } const flattenDescendants = (node: Node): SimplifiedNode[] => { return node.children.length === 0 ? [] : node.children.flatMap(child => [ { name: child.name, id: child.id, count: child.count }, ...flattenDescendants(child) ]); }; const findNodeById = (nodes: Node[], id: string): Node | undefined => { return nodes.reduce((found, node) => { if (found) return found; if (node.id === id) return node; return findNodeById(node.children, id); }, undefined); }; const getTargetNodes = (categoryIds?: string[]): SimplifiedNode[] => { if (!categoryIds || categoryIds.length === 0) { return data.flatMap(node => [ { name: node.name, id: node.id, count: node.count }, ...node.children.map(child => ({ name: child.name, id: child.id, count: child.count })) ]); } return categoryIds.flatMap(id => { const targetNode = findNodeById(data as Node[], id); return targetNode ? flattenDescendants(targetNode) : []; }); };
4. 测试验证
- 传入
['22', '3']:返回预期的3个子节点数组 - 不传入参数:返回父节点+直接子节点的数组
- 传入无子女节点的ID(如
['344']):返回空数组[]
内容的提问来源于stack exchange,提问作者CEJ
相关产品推荐
相关产品推荐

