You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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":""}]}]}]}]}

解决方案:递归过滤函数

之前尝试的扁平化处理、单层级属性过滤无法满足需求,因为需要保留完整的父节点树形结构。以下是递归实现的核心逻辑:

实现思路

  1. 递归遍历每个节点,先处理当前节点的所有子节点(tree数组)
  2. 对每个子节点递归调用过滤函数,得到过滤后的子节点集合
  3. 判断当前节点是否需要保留:
    • 自身path包含搜索关键词,或过滤后的子节点集合不为空(说明子节点有匹配项)
  4. 若需要保留,则返回当前节点的副本(避免修改原数据),并将过滤后的子节点赋值给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 09:35:22