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

如何遍历未知深度树状嵌套结构 按路径查找目标数组项

实现思路

要实现任意长度路径的类文件树遍历,同时支持节点弹出、移动这类修改操作,不能只返回路径匹配到的目标节点——必须在逐层往下匹配的过程中,同步记录目标节点的直接父节点、节点在父节点folders数组中的索引、父节点folders数组的直接引用,否则后续修改时找不到节点的上级位置,操作没法落地。

具体实现

首先写通用的路径查找函数,兼容任意层级嵌套、任意长度的路径输入:

// 示例根文件树,就是你给出的结构
const rootTree = [{
  "name": "Audiograms",
  "folders": [{
    "name": "2022"
  }, {
    "name": "2021"
  }, {
    "name": "2020"
  }]
}, {
  "name": "Patient Paperwork"
}, {
  "name": "Repairs"
}]

/**
 * 按路径查找文件树节点
 * @param {Array} rootTree 根层级的文件/文件夹数组
 * @param {Array<string>} path 路径数组,比如["Audiograms", "2022"]
 * @returns {Object} 查找结果
 */
function findNodeByPath(rootTree, path) {
  let currentLevel = rootTree;
  let parentNode = null;
  let targetIndex = -1;

  // 路径为空直接返回失败
  if (!path.length) return { success: false, node: null, parentLevel: null, index: -1 }

  for (let i = 0; i < path.length; i++) {
    const currentName = path[i];
    // 在当前层级找名称匹配的项
    targetIndex = currentLevel.findIndex(item => item.name === currentName);
    if (targetIndex === -1) {
      return { success: false, node: null, parentLevel: null, index: -1 }
    }
    const currentNode = currentLevel[targetIndex];
    // 走到路径最后一位,就是目标节点
    if (i === path.length - 1) {
      return {
        success: true,
        node: currentNode,
        parentNode: parentNode,
        parentLevel: currentLevel,
        index: targetIndex
      }
    }
    // 没到路径末尾,往下一层走,当前节点没有子文件夹就说明路径无效
    if (!currentNode.folders || !Array.isArray(currentNode.folders)) {
      return { success: false, node: null, parentLevel: null, index: -1 }
    }
    parentNode = currentNode;
    currentLevel = currentNode.folders;
  }
}

弹出(删除)匹配节点

查找成功后,直接利用返回的父级数组引用和索引,用splice删除即可,被删除的节点可以直接拿到用于后续操作:

const targetPath = ["Audiograms", "2022"];
const findResult = findNodeByPath(rootTree, targetPath);
if (findResult.success) {
  // 删除对应索引的项,拿到被弹出的节点
  const [removedNode] = findResult.parentLevel.splice(findResult.index, 1);
  console.log('弹出的节点:', removedNode);
}

移动节点到其他位置

移动逻辑本质是「原位置弹出节点 + 节点插入到目标父文件夹的folders数组」,注意提前做循环引用校验,避免把文件夹移动到自身子目录下造成死循环:

/**
 * 移动节点到目标路径
 * @param {Array} rootTree 根文件树
 * @param {Array} sourcePath 原节点路径
 * @param {Array} targetParentPath 目标父文件夹路径
 */
function moveNode(rootTree, sourcePath, targetParentPath) {
  // 校验源节点是否存在
  const sourceResult = findNodeByPath(rootTree, sourcePath);
  if (!sourceResult.success) throw new Error('源路径不存在');
  // 校验目标父文件夹是否存在
  const targetResult = findNodeByPath(rootTree, targetParentPath);
  if (!targetResult.success) throw new Error('目标路径不存在');

  const sourceNode = sourceResult.node;
  // 简单循环校验:不能把节点移动到自身目录下
  if (sourceNode === targetResult.node) throw new Error('不能将节点移动到自身目录下');

  // 从原位置删除节点
  const [movedNode] = sourceResult.parentLevel.splice(sourceResult.index, 1);
  // 目标文件夹如果没有子文件夹列表就初始化
  if (!targetResult.node.folders) targetResult.node.folders = [];
  // 把节点加到目标文件夹下
  targetResult.node.folders.push(movedNode);
}

// 调用示例:把Audiograms/2022移动到Repairs文件夹下
moveNode(rootTree, ["Audiograms", "2022"], ["Repairs"])
注意事项
  • 如果业务中允许不同文件夹下存在同名文件夹/文件,不要用name做匹配键,给每个节点加全局唯一的ID,路径数组存ID序列,匹配准确度会高很多
  • 移动节点一定要做循环引用校验,绝对不能把一个文件夹移动到它自己、或者它自己的子文件夹下面,不然文件树会出现循环嵌套,后续遍历直接栈溢出
  • 如果需要支持移动节点到根目录,补全查找函数对空路径的处理即可:空路径对应的父级数组就是根目录数组,没有父节点
  • 如果要把节点插到目标文件夹的指定位置,而不是默认加到列表末尾,移动时多传一个目标位置索引,用splice(insertIndex, 0, 待移动节点)就能完成指定位置插入

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 11:31:11