树结构过滤(查找)数据,父级一起带出来。

搜索到的结果 

树形结构数据的高效过滤与父级关联方案 

问题场景:树形数据的智能搜索

在实际开发中,我们经常需要处理树形结构数据的搜索过滤,并且要求能够同时返回匹配节点及其所有父级节点。这种需求在文件系统、组织架构、分类目录等场景中十分常见。本文将深入分析一个树形数据过滤方案,并提供多种优化思路。

基础实现方案分析

直接上代码。。。。

/**
   * @author 过滤数据
   * @param {*} value
   * @param {*} arr
   * @return {*} []
   */
  const TreeDataList = (value:any = '', arr:any) => {
    if (!arr) {
      return []
    }
    let newList:any[] = [];
    arr.forEach((item:any) => {
      if (item.title1.indexOf(value) > -1) {
        const Children = TreeDataList(value, item.children);
        const obj = {
          ...item,
          children: Children
        }
        console.log(item,obj,Children);
        newList.push(obj);
      } else {
        if (item.children && item.children.length > 0) {
          const Children = TreeDataList(value, item.children);
          const obj = {
            ...item,
            children: Children
          };
          if (Children && Children.length > 0) {
            newList.push(obj);
          }
        }
      }
    });
    return newList;
  };

当前实现特点:

  1. 递归遍历树形结构

  2. 匹配包含关键词的节点(title1)

  3. 自动保留有效子节点的父级

  4. 返回保持原有结构的新树

优化方案一:性能提升版

const filterTree = (keyword = '', treeData = []) => {
  if (!treeData.length) return [];
  
  return treeData.reduce((result, node) => {
    // 检查当前节点是否匹配
    const isMatch = node.title1.includes(keyword);
    
    // 递归处理子节点
    const children = filterTree(keyword, node.children || []);
    
    // 如果当前节点匹配或有匹配的子节点,则保留该节点
    if (isMatch || children.length) {
      result.push({
        ...node,
        children: children.length ? children : undefined
      });
    }
    
    return result;
  }, []);
};

优化点:

  1. 使用reduce替代forEach,减少中间变量

  2. 简化条件判断逻辑

  3. 使用includes替代indexOf,语义更清晰

  4. 空子节点设为undefined而非空数组,减少内存占用

优化方案二:支持多字段搜索

const filterTreeAdvanced = (
  keyword = '', 
  treeData = [],
  searchFields = ['title1', 'title2', 'code']
) => {
  return treeData.filter(node => {
    // 检查当前节点是否匹配任意搜索字段
    const isNodeMatch = searchFields.some(
      field => node[field]?.toString().includes(keyword)
    );
    
    // 递归处理子节点
    const matchedChildren = filterTreeAdvanced(
      keyword, 
      node.children || [], 
      searchFields
    );
    
    // 保留当前节点条件
    return isNodeMatch || matchedChildren.length;
  }).map(node => ({
    ...node,
    children: filterTreeAdvanced(keyword, node.children || [], searchFields)
  }));
};

增强功能:

  1. 支持多个字段的搜索

  2. 自动处理字段值为非字符串的情况

  3. 更简洁的函数式编程风格

优化方案三:非递归实现(避免栈溢出)

const filterTreeIterative = (keyword, treeData) => {
  const stack = [...treeData.map(node => ({ node, parent: null }))];
  const result = [];
  
  while (stack.length) {
    const { node, parent } = stack.pop();
    
    const isMatch = node.title1.includes(keyword);
    let shouldKeep = isMatch;
    
    // 处理子节点
    const children = (node.children || []).map(child => ({
      node: child,
      parent: node
    }));
    
    // 如果有匹配的子节点,当前节点也需要保留
    const hasMatchingChildren = children.some(
      ({ node: child }) => child.title1.includes(keyword)
    );
    shouldKeep = shouldKeep || hasMatchingChildren;
    
    if (shouldKeep) {
      const newNode = { ...node, children: [] };
      if (parent) {
        parent.children.push(newNode);
      } else {
        result.push(newNode);
      }
    }
    
    // 将子节点压入栈中
    stack.push(...children);
  }
  
  return result;
};

优势:

  1. 使用迭代替代递归,避免栈溢出风险

  2. 显式处理父子关系,逻辑更清晰

  3. 适合处理深度很大的树结构

性能对比

方案 优点 缺点 适用场景
基础递归 实现简单 性能一般,可能栈溢出 小规模数据
优化递归 代码简洁 仍存在递归风险 中等规模数据
多字段搜索 功能强大 性能开销稍大 需要多字段搜索
迭代实现 无栈溢出风险 代码较复杂 超深树结构

最佳实践建议

  1. 数据规模考虑

    • <1000节点:递归方案即可

    • 1000节点:考虑迭代实现

  2. 搜索体验优化

    • 添加防抖处理(300ms)

    • 支持模糊搜索(考虑正则表达式)

    • 高亮显示匹配内容

总结

树形数据的过滤搜索是一个常见但具有挑战性的需求。本文从基础实现出发,逐步介绍了多种优化方案,每种方案都有其适用场景。在实际项目中,建议:

  1. 根据数据规模选择合适算法

  2. 考虑添加缓存机制优化重复搜索

  3. 做好错误处理和性能监控

  4. 在用户体验和性能之间取得平衡

希望这些方案能为你的树形数据搜索需求提供有价值的参考!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐