树形结构数据查找某个 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即可

相关推荐
拾年27512 小时前
React Hooks 进阶篇:useMemo / useCallback / useReducer / useImperativeHandle,用「算账 +
前端·javascript·react.js
Jackson__12 小时前
面试官:如何在老项目中使用新框架,并实现通信?
前端·javascript·面试
拾年27512 小时前
React Hooks 保姆级教程:把组件想象成失忆的打工人,4 个 Hook 带你原地起飞
前端·javascript·react.js
mmsx12 小时前
一个 Bug 修了 12 轮,根因只有一句话:AI 编程时代,日志才是唯一的真相
前端
默_笙12 小时前
😝 5 亿次循环卡死页面?我用 Web Worker 把它扔进后台线程,页面丝滑如初
前端·javascript
CAD老兵12 小时前
一行代码集成 DWG/DXF 图纸查看:测量批注,数据不出站
前端·javascript·github
小林ixn12 小时前
单例模式:从弹窗管理到全局状态,一个模式搞定
前端·javascript·设计模式
AI编程实验室12 小时前
Node.js Agent Handoff 仓库扫描 MVP:忽略规则、include 通配与稳定输出实现
前端·ai编程
猫不易12 小时前
从 Virtual DOM 到 Vapor:Vue 3.6 的另一条路
前端·vue.js
光影少年12 小时前
react navite性能优化 & 常见坑
前端·react native·掘金·金石计划