树形结构数据查找某个 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,或者定义一个参数 dataItem[keyName]即可

相关推荐
奇迹_h4 小时前
打造你的HTML5打地鼠游戏:零基础入门实践
前端
SuperEugene4 小时前
Vue生态精选篇:Element Plus 的“企业后台常用组件”用法扫盲
前端·vue.js·面试
Neptune14 小时前
JavaScript回归基本功之---类型判断--typeof篇
前端·javascript·面试
贾铭4 小时前
如何实现一个网页版的剪映(三)使用fabric.js绘制时间轴
前端·后端
进击的尘埃4 小时前
微前端沙箱隔离:qiankun 和 wujie 到底在争什么
javascript
子兮曰5 小时前
后端字段又改了?我撸了一个 BFF 数据适配器,从此再也不怕接口“屎山”!
前端·javascript·架构
万少7 小时前
使用Trae轻松安装openclaw的教程-附带免费token
前端·openai·ai编程
颜酱7 小时前
一步步实现字符串计算器:从「转整数」到「带括号与优化」
javascript·后端·算法
浪浪山_大橙子7 小时前
OpenClaw 十分钟快速,安装与接入完全指南 - 推荐使用trae 官方 skills 安装
前端·人工智能
忆江南7 小时前
iOS 可视化埋点与无痕埋点详解
前端