JavaScript 递归获取树节点 ID

在树形结构数据中,经常需要根据某个节点 ID 递归查找其所有子节点、父节点或兄弟节点的 ID。以下提供几个常用的工具函数。


获取指定节点及其所有子节点的 ID

传入目标 id 时,先找到该节点,再递归收集其所有后代节点 ID;不传 id(或传 false)时,直接收集所有节点 ID。

function getId(arr, id, ids = []) {
  if (id) {
    for (let item of arr) {
      if (item.id == id) {
        ids.push(item.id);
        // 找到目标节点后,递归收集其所有子节点
        getId(item.children || [], false, ids);
      } else {
        getId(item.children || [], id, ids);
      }
    }
  } else {
    // 递归收集全部节点 ID
    for (let item of arr) {
      ids.push(item.id);
      if (item.children) {
        getId(item.children, false, ids);
      }
    }
  }
  return ids;
}

获取所有父节点 ID(路径)

从根节点向下递归查找目标节点,返回从根到目标节点的完整路径(包含目标节点自身的 ID)。

/**
 * 查找目标节点的祖先路径
 * @param {string|number} childrenId 目标子节点 ID
 * @param {Array} arr 树形数据
 * @param {Array} path 当前路径(递归内部使用)
 * @returns {Array|undefined} 路径数组,未找到返回 undefined
 */
export const findParents = (childrenId, arr, path = []) => {
  for (let i = 0; i < arr.length; i++) {
    // 复制当前路径,避免污染其他分支
    let tmpPath = path.concat();
    tmpPath.push(arr[i].id);

    if (childrenId == arr[i].id) {
      return tmpPath; // 找到目标,返回路径
    }

    if (arr[i].children) {
      let findResult = findParents(childrenId, arr[i].children, tmpPath);
      if (findResult) {
        return findResult;
      }
    }
  }
};

获取指定节点的所有子节点 ID

递归查找目标节点,收集其所有后代节点的 ID(包含目标节点自身)。

/**
 * 查找指定节点及其所有子节点的 ID
 * @param {string|number|false} id 目标节点 ID,false 表示直接收集当前层所有节点
 * @param {Array} tree 树形数据
 * @param {Array} temporary 结果数组(递归内部使用)
 * @returns {Array} ID 数组
 */
export const findChildren = (id, tree, temporary = []) => {
  if (!id) {
    // 收集当前数组的所有节点及其子节点
    tree.forEach((item) => {
      temporary.push(item.id);
      if (item.children) {
        findChildren(false, item.children, temporary);
      }
    });
    return temporary;
  }

  tree.forEach((item) => {
    if (item.id === id) {
      temporary.push(item.id);
      if (item.children) {
        findChildren(false, item.children, temporary);
      }
    } else {
      if (item.children) {
        findChildren(id, item.children, temporary);
      }
    }
  });

  return temporary;
};

获取所有兄弟节点 ID

找到目标节点所在的层级,返回该层级中所有节点的 ID(包含目标节点自身)。

/**
 * 查找目标节点的所有兄弟节点 ID(含自身)
 * @param {string|number} id 目标节点 ID
 * @param {Array} tree 树形数据
 * @param {Array} temporary 结果数组(递归内部使用)
 * @returns {Array} 同级所有节点的 ID 数组
 */
export const findSiblings = (id, tree, temporary = []) => {
  // 判断目标节点是否在当前层级
  if (tree.find((item) => item.id === id)) {
    tree.forEach((item) => temporary.push(item.id));
    return temporary;
  }

  // 向子节点递归查找
  tree.forEach((item) => {
    if (item.children) {
      findSiblings(id, item.children, temporary);
    }
  });

  return temporary;
};

获取节点名称

根据 ID 在树中查找对应节点,返回其名称字段(此处为 dictValue,可按实际字段名修改)。

/**
 * 根据 ID 查找节点名称
 * @param {string|number} id 目标节点 ID
 * @param {Array} tree 树形数据
 * @param {Array} temporary 结果数组(递归内部使用)
 * @returns {string|undefined} 节点名称,未找到返回 undefined
 */
export const findNodeName = (id, tree = [], temporary = []) => {
  tree.forEach((item) => {
    if (item.id == id) {
      temporary.push(item.dictValue);
    } else if (item.children) {
      findNodeName(id, item.children, temporary);
    }
  });

  return temporary[0];
};