如何在React/JavaScript中递归过滤树形JSON数据?
树形JSON递归过滤实现(按path字段包含搜索)
需求说明
需要遍历树形JSON的所有层级(从Customers节点开始),通过字符串包含逻辑对path字段进行过滤,最终保留匹配节点及其所有父节点的完整树形结构,而非仅扁平化结果或单层级过滤。
原始数据结构
{"path":"Customers","sha":"Customers","lazy":false,"type":"tree","tree":[{"path":"Bob Rivers","type":"tree","sha":"Bob Rivers","lazy":false,"tree":[{"path":"Services","type":"tree","sha":"Services","lazy":true,"url":"http://localhost:3000/services","tree":[{"path":"Service_X","type":"tree","sha":"Service_X","lazy":true,"url":"http://localhost:3000/Service_X","tree":[{"lazy":true,"path":"Service_X_child","mode":"040000","type":"tree","sha":"Service_X_child","url":"http://localhost:3000/Service_X_child","tree":[{"lazy":true,"path":"ABC","mode":"040000","type":"blob","sha":"ABC","url":""},{"lazy":true,"path":"DEF","mode":"040000","type":"blob","sha":"DEF","url":""}]},{"lazy":true,"path":"Service_X_child_2","mode":"040000","type":"tree","sha":"Service_X_child_2","url":"http://localhost:3000/Service_X_child_2"}]},{"path":"Service_Y","type":"tree","sha":"Service_Y","lazy":true,"url":"http://localhost:3000/Service_Y"}]}]}]}
过滤示例(搜索关键词"DEF")
{"path":"Customers","sha":"Customers","lazy":false,"type":"tree","tree":[{"path":"Bob Rivers","type":"tree","sha":"Bob Rivers","lazy":false,"tree":[{"path":"Services","type":"tree","sha":"Services","lazy":true,"url":"http://localhost:3000/services","tree":[{"path":"Service_X","type":"tree","sha":"Service_X","lazy":true,"url":"http://localhost:3000/Service_X","tree":[{"lazy":true,"path":"Service_X_child","mode":"040000","type":"tree","sha":"Service_X_child","url":"http://localhost:3000/Service_X_child","tree":[{"lazy":true,"path":"DEF","mode":"040000","type":"blob","sha":"DEF","url":""}]}]}]}]}
解决方案:递归过滤函数
之前尝试的扁平化处理、单层级属性过滤无法满足需求,因为需要保留完整的父节点树形结构。以下是递归实现的核心逻辑:
实现思路
- 递归遍历每个节点,先处理当前节点的所有子节点(
tree数组) - 对每个子节点递归调用过滤函数,得到过滤后的子节点集合
- 判断当前节点是否需要保留:
- 自身
path包含搜索关键词,或过滤后的子节点集合不为空(说明子节点有匹配项)
- 自身
- 若需要保留,则返回当前节点的副本(避免修改原数据),并将过滤后的子节点赋值给
tree字段;否则返回null(不保留该节点)
代码实现(JavaScript)
function filterTree(node, keyword) { // 先递归处理所有子节点 const filteredChildren = (node.tree || []) .map(child => filterTree(child, keyword)) .filter(Boolean); // 过滤掉返回null的节点 // 判断当前节点是否需要保留:自身path匹配 或 有匹配的子节点 const shouldKeep = node.path.includes(keyword) || filteredChildren.length > 0; if (!shouldKeep) { return null; } // 返回节点副本,避免修改原数据 return { ...node, tree: filteredChildren }; } // 使用示例 const originalData = {"path":"Customers","sha":"Customers","lazy":false,"type":"tree","tree":[{"path":"Bob Rivers","type":"tree","sha":"Bob Rivers","lazy":false,"tree":[{"path":"Services","type":"tree","sha":"Services","lazy":true,"url":"http://localhost:3000/services","tree":[{"path":"Service_X","type":"tree","sha":"Service_X","lazy":true,"url":"http://localhost:3000/Service_X","tree":[{"lazy":true,"path":"Service_X_child","mode":"040000","type":"tree","sha":"Service_X_child","url":"http://localhost:3000/Service_X_child","tree":[{"lazy":true,"path":"ABC","mode":"040000","type":"blob","sha":"ABC","url":""},{"lazy":true,"path":"DEF","mode":"040000","type":"blob","sha":"DEF","url":""}]},{"lazy":true,"path":"Service_X_child_2","mode":"040000","type":"tree","sha":"Service_X_child_2","url":"http://localhost:3000/Service_X_child_2"}]},{"path":"Service_Y","type":"tree","sha":"Service_Y","lazy":true,"url":"http://localhost:3000/Service_Y"}]}]}]}; const filteredResult = filterTree(originalData, "DEF"); console.log(JSON.stringify(filteredResult));
关键说明
- 递归处理子节点:确保所有层级都被遍历,不会遗漏深层节点
- 保留父节点:只要子节点有匹配项,父节点就会被保留,保证树形结构完整
- 不修改原数据:通过对象展开运算符(
...node)创建节点副本,避免污染原始数据 - 大小写敏感:若需要忽略大小写,可将判断逻辑改为
node.path.toLowerCase().includes(keyword.toLowerCase())
内容的提问来源于stack exchange,提问作者Nate
相关产品推荐
相关产品推荐

