如何在JS/TS中获取深层嵌套数组的指定节点直接子项
树形结构节点查询实现方案(JS/TS)
原始数据
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(如
['101', '3'])时,返回对应节点的直接子项,仅保留name、id、count字段 - 未传入ID时,默认返回所有一级节点及其直接子项,同样保留指定字段
- 若传入的ID无直接子项则返回空数组
- 禁止使用
for、forEach、while循环,支持任意深度的层级结构
JS实现方案
// 递归查找目标ID节点的直接子项 const findNodeById = (nodes, targetIds) => { return nodes.flatMap(node => { // 当前节点匹配目标ID则取出其子项,否则取空数组 const matches = targetIds.includes(node.id) ? node.children : []; // 递归处理子节点,确保深层级ID被匹配 const childMatches = findNodeById(node.children, targetIds); return [...matches, ...childMatches]; }); }; // 格式化节点,只保留需要的字段 const formatNode = node => ({ name: node.name, id: node.id, count: node.count }); // 主查询函数 const getTargetChildren = (sourceData, targetIds = null) => { if (!targetIds) { // 未传ID时,返回所有一级节点+它们的直接子项 return sourceData.flatMap(node => [ formatNode(node), ...node.children.map(formatNode) ]); } // 传ID时,获取匹配节点的直接子项并格式化 const matchedChildren = findNodeById(sourceData, targetIds); return matchedChildren.map(formatNode); };
测试用例
- 传入ID列表
['101', '3']:
console.log(getTargetChildren(data, ['101', '3'])); // 输出: // [ // { name: "Engine and Brakes", id: "344", count: "1" }, // { name: "SpeedBike", id: "4", count: "12" } // ]
- 未传入ID:
console.log(getTargetChildren(data)); // 输出: // [ // { name: "Car", id: "19", count: "20" }, // { name: "Wheel", id: "22", count: "3" }, // { name: "Bike", id: "3", count: "12" }, // { name: "SpeedBike", id: "4", count: "12" } // ]
- 传入无子女的ID(如
['344']):
console.log(getTargetChildren(data, ['344'])); // 输出:[]
TS实现方案
// 定义节点类型 interface TreeNode { name: string; id: string; count: string; depth: string; children: TreeNode[]; } // 定义格式化后的节点类型 interface FormattedNode { name: string; id: string; count: string; } // 原始数据(带类型标注) const data: TreeNode[] = [ { 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节点的直接子项 const findNodeById = (nodes: TreeNode[], targetIds: string[]): TreeNode[] => { return nodes.flatMap(node => { const matches = targetIds.includes(node.id) ? node.children : []; const childMatches = findNodeById(node.children, targetIds); return [...matches, ...childMatches]; }); }; // 格式化节点 const formatNode = (node: TreeNode): FormattedNode => ({ name: node.name, id: node.id, count: node.count }); // 主查询函数 const getTargetChildren = (sourceData: TreeNode[], targetIds?: string[]): FormattedNode[] => { if (targetIds === undefined) { return sourceData.flatMap(node => [ formatNode(node), ...node.children.map(formatNode) ]); } const matchedChildren = findNodeById(sourceData, targetIds); return matchedChildren.map(formatNode); };
实现逻辑说明
findNodeById:通过flatMap递归遍历所有节点,匹配到目标ID时取出其直接子项,同时递归处理子节点,确保深层级ID也能被找到formatNode:统一处理节点字段,过滤掉不需要的属性getTargetChildren:分场景处理请求:- 未传ID时,用
flatMap将一级节点和其子项合并为一个数组后格式化 - 传ID时,调用递归函数获取所有匹配子项,再格式化返回
- 未传ID时,用
- 全程使用
flatMap、map、includes等方法替代传统循环,满足要求
内容的提问来源于stack exchange,提问作者CEJ
相关产品推荐
相关产品推荐

