树形结构数据查找某个 key 的路径

需求

假设有一个树形数据treeData

javascript 复制代码
const treeData = [
    { key: 1, title: 'node1'},
    { key: 2, title: 'node2', children: 
        [
            { key: 3, title: 'node3', children: 
                [
                    { key: 4, title: 'node4' }
                ]
            }
         ]
     }
]

大概就是这么个数据,现在给出一个 key,可以得到该节点的路径。

例如给出的 key 为4,那么期望得到 1, 'children', 0, 'children', 0,奇数部分代表 children 中的 index,偶数部分固定是 'children',可以看到得到这个 path 数组后可以直接拿到这个节点的数据,比如使用 lodash 中的 get 方法,或者循环 path 取值。

思路

直接看一下完整代码

javascript 复制代码
function findKeyPath(treelikeData, targetKey, childrenKey = 'children') {
  const recursiveSearch = (_treelikeData = []) => {
    let path = [];
    const hasKey = _treelikeData.some((dataItem, index) => {
      if (dataItem.key === targetKey) {
        path.push(index);
        return true;
      }
      if (Array.isArray(dataItem[childrenKey])) {
        const searchResult = recursiveSearch(dataItem[childrenKey]);
        if (searchResult.hasKey) {
          path = [...path, index, childrenKey, ...searchResult.path];
        }
        return searchResult.hasKey;
      }
      return false;
    });
    return { hasKey, path };
  };

  const searchResult = recursiveSearch(treelikeData);
  return searchResult.path;
}

我的思路是这样的,先写一个递归的搜索函数recursiveSearch,这个函数会返回hasKey 和 path 两个数据,path 是最终要得到的路径,hasKey是用来判断是否找到了 key。

用Array 的 some 函数来遍历数据的原因是,当返回了 true 之后,遍历会立即停止,这样能节省些开销,且 some 的返回结果就是 true 或 false,正好可以用来判断是否找到了。

在查找判断是否有 key 时,先排除 2 种边界情况:

  1. 如果dataItem.key === targetKey,就直接返回 true 并且把该数据的 index 添加进 path 中,这是已经递归到最末端的情况了。
  2. 排除 1 情况后,遍历出来的当前item,没有子数组了,说明这一枝并没有要寻找的数据,直接返回 false

接下来就是需要递归的部分了,此时复杂的情况已经简化了,首先递归查找当前数据的子数组中是否有要查找的数据,就像上面说的那样,递归搜索函数的返回结果是path和 hasKey,这时候判断一下hasKey是true还是false,如果是true的话,就将子数组返回的path和当前的path合并(中间加上当前的index和 'children')

使用

javascript 复制代码
const keyPath = findKeyPath(treeData, '4')
//  [1, 'children',  0, 'children',  0]

ps: 其实如果数据是后端传来的,可能返回的是 { id: 1, children: \[ }] 这样的数据,这部分取值时修改成 dataItem.key,或者定义一个参数 dataItemkeyName即可

相关推荐
孟健1 小时前
Claude Code 同需求写两版对比:装不装 frontend-design 差多远?
前端·ai编程·claude
OpsEye2 小时前
大模型 Token 计量不准,是什么原因,怎么解决??
javascript·ai编程
梦de天空一米16883 小时前
【一个重复id让游戏卡在62%,聊聊配置表检查】
前端
ryn483983 小时前
01 一个功能模块,怎么分层才不乱
前端·ai编程·claude
Amos_Web3 小时前
通过 Rspack 源码学 Rust(五):读懂 Result 与问号运算符的错误返回
前端
杨航 AI3 小时前
JEV的用法
java·前端·数据库
Amos_Web3 小时前
Rspack 源码解析(三十五):rspack_error 如何用 miette 渲染带源码定位的错误链
前端
去伪存真3 小时前
高敏资产场景下的大模型协同设计架构
前端·agent
修炼的dance3 小时前
OCR预处理测了108组,纠斜去噪有用,坑在顺序和力度
前端
疯华整猫3 小时前
Android 16 后台常驻适配:用 Trae 搭一套“前台服务 + 加急 Job”的可存活架构
前端