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

如何在嵌套对象中搜索字符串并更新所有父子节点的属性

如何给层级树节点设置matchFound属性(匹配自身/父/子节点时设为true)

我有一个层级结构的对象数组,现在需要实现:根据指定搜索字符串,更新每个节点的matchFound属性——只要节点自身、父节点或任意子节点包含该搜索字符串,就把matchFound设为true,否则设为false。之前已经实现了过滤数组的功能,但现在想直接修改原树的属性,而不是生成新的过滤数组。

原数据结构及旧实现代码:

const treeData = [{
    name: 'Infiniti',
    matchFound: null,
    children: [{
        name: 'G50',
        matchFound: null,
        children: [{
            name: 'Pure AWD',
            matchFound: null,
          },
          {
            name: 'Luxe',
            matchFound: null,
          },
        ],
      },
      {
        name: 'QX50',
        matchFound: null,
        children: [{
            name: 'Pure AWD',
            matchFound: null,
          },
          {
            name: 'Luxe',
            matchFound: null,
          },
        ],
      },
    ],
  },
  {
    name: 'BMW',
    matchFound: null,
    children: [{
        name: '2 Series',
        matchFound: null,
        children: [{
            name: 'Coupé',
            matchFound: null,
          },
          {
            name: 'Gran Coupé',
            matchFound: null,
          },
        ],
      },
      {
        name: '3 Series',
        matchFound: null,
        children: [{
            name: 'Sedan',
            matchFound: null,
          },
          {
            name: 'PHEV',
            matchFound: null,
          },
        ],
      },
    ],
  },
];

let filteredData = [];

function filter(searchString) {
  this.filteredData = this.search(this.treeData, searchString);
  console.log(this.filteredData)
}

function search(children, searchString) {
  return children.reduce((acc, item) => {
    if (item.name.toLowerCase().includes(searchString.toLowerCase())) {
      acc.push(item);
    } else if (item.children && item.children.length > 0) {
      const newItems = this.search(item.children, searchString);
      if (newItems.length > 0) {
        acc.push({
          name: item.name,
          children: newItems
        });
      }
    }
    return acc;
  }, []);
}

this.filter('infiniti');
this.filter('luxe');

解决方案

要实现直接更新节点matchFound的需求,需要分两次遍历树结构:

  • 后序遍历:从叶子节点往上,先判断子节点是否匹配,再结合自身匹配状态,设置当前节点的初始matchFound值
  • 前序遍历:从根节点往下,把父节点的匹配状态传递给子节点——如果父节点是true,子节点也必须设为true

完整实现代码:

const treeData = [{
    name: 'Infiniti',
    matchFound: null,
    children: [{
        name: 'G50',
        matchFound: null,
        children: [{
            name: 'Pure AWD',
            matchFound: null,
          },
          {
            name: 'Luxe',
            matchFound: null,
          },
        ],
      },
      {
        name: 'QX50',
        matchFound: null,
        children: [{
            name: 'Pure AWD',
            matchFound: null,
          },
          {
            name: 'Luxe',
            matchFound: null,
          },
        ],
      },
    ],
  },
  {
    name: 'BMW',
    matchFound: null,
    children: [{
        name: '2 Series',
        matchFound: null,
        children: [{
            name: 'Coupé',
            matchFound: null,
          },
          {
            name: 'Gran Coupé',
            matchFound: null,
          },
        ],
      },
      {
        name: '3 Series',
        matchFound: null,
        children: [{
            name: 'Sedan',
            matchFound: null,
          },
          {
            name: 'PHEV',
            matchFound: null,
          },
        ],
      },
    ],
  },
];

// 后序遍历:先处理子节点,再设置当前节点的matchFound(自身匹配或子节点匹配)
function updateMatchFromChildren(node, searchStr) {
  const lowerSearch = searchStr.toLowerCase();
  let hasMatch = node.name.toLowerCase().includes(lowerSearch);
  
  if (node.children && node.children.length > 0) {
    // 遍历所有子节点,只要有一个子节点匹配,当前节点就标记为匹配
    node.children.forEach(child => {
      if (updateMatchFromChildren(child, searchStr)) {
        hasMatch = true;
      }
    });
  }
  
  node.matchFound = hasMatch;
  return hasMatch;
}

// 前序遍历:把父节点的匹配状态传递给子节点(父节点为true,子节点也设为true)
function propagateMatchFromParent(node, parentMatch) {
  // 如果父节点匹配,当前节点也必须匹配
  if (parentMatch) {
    node.matchFound = true;
  }
  
  if (node.children && node.children.length > 0) {
    node.children.forEach(child => {
      propagateMatchFromParent(child, node.matchFound);
    });
  }
}

// 主函数:执行两次遍历更新matchFound
function updateMatchFound(searchString) {
  // 重置所有matchFound为初始值null(避免上次搜索结果干扰)
  treeData.forEach(node => {
    function reset(node) {
      node.matchFound = null;
      if (node.children) node.children.forEach(reset);
    }
    reset(node);
  });
  
  // 第一步:从子到根设置匹配状态
  treeData.forEach(node => updateMatchFromChildren(node, searchString));
  // 第二步:从根到子传递父节点的匹配状态
  treeData.forEach(node => propagateMatchFromParent(node, false));
  
  console.log('更新后的树结构:', treeData);
}

// 测试调用
updateMatchFound('infiniti');
// updateMatchFound('luxe');

代码说明

  1. updateMatchFromChildren:递归遍历子节点,先处理所有子节点的匹配状态,再判断当前节点自身是否匹配。只要子节点中有一个匹配,当前节点的matchFound就设为true。
  2. propagateMatchFromParent:递归遍历子节点,如果父节点的matchFound是true,就把当前节点的matchFound强制设为true——这一步保证了父节点匹配时,所有子节点都会被标记为匹配。
  3. updateMatchFound:主函数,先重置所有节点的matchFound,然后执行两次遍历完成更新。

测试updateMatchFound('infiniti')时,Infiniti节点及其所有子节点的matchFound都会变成true;测试updateMatchFound('luxe')时,所有名为Luxe的节点、它们的父节点(G50、QX50)、以及根节点Infiniti的matchFound都会变成true,而BMW相关节点则为false。

内容的提问来源于stack exchange,提问作者Jason22

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:20:32