首页 > 解决方案 > 根据id递归获取树的段

问题描述

我正在尝试基于 id 获取树段。id 可能位于根目录,也可能位于子节点的任何位置。我的目标是获得整个家谱,而不是其他不相关的数据。

我有整个数据树和一个 ID。

这个想法是递归地执行此操作,因为孩子的数量是未知的。

import "./styles.css";

export default function App() {
  const folderTree = [
    {
      id: "1-1",
      children: [
        {
          id: "1-2",
          parentId: "1-1",
          children: []
        }
      ]
    },
    {
      id: "2-1",
      children: [
        {
          id: "2-2",
          parentId: "2-1",
          children: [
            {
              id: "2-4",
              parentId: "2-2",
              children: []
            }
          ]
        },
        {
          id: "2-3",
          parentId: "2-1",
          children: []
        }
      ]
    }
  ];

  const getRelatedTreeFolders = (folders, selectedFolderId) => {
    //** goes top to bottom

    const recursiveChildCheck = (folder, id) => {
      // THIS trial failed
      // let foundNested = false;
      // if (folder.id === id) {
      //   return true;
      // }
      // function recurse(folder) {
      //   if (!folder.hasOwnProperty("children") || folder.children.length === 0)
      //     return;
      //   for (var i = 0; i < folder.children.length; i++) {
      //     if (folder.children[i].id === id) {
      //       foundNested = true;
      //       break;
      //     } else {
      //       if (folder.children[i].children.length > 0) {
      //         recurse(folder.children[i].children);
      //         if (foundNested) {
      //           break;
      //         }
      //       }
      //     }
      //   }
      // }
      // recurse(folder);
      // return foundNested;

      const aChildHasIt =
        folder.children.length > 0 && folder.children.some((f) => f.id === id);
      if (aChildHasIt) return true;

      let nestedChildHasIt = false;
      /** The problem seems to be here */
      folder.children.forEach((childFolder) => {
        // Is using a forEach loop the correct way?
        // ideally it seems there is a simple way to do a recursive .some on the dhildren...
        childFolder.children.length>0 && recursiveChildCheck(childFolder, id)
      });
      if (nestedChildHasIt) return true;
      folder.children && folder.children.forEach(recursiveChildCheck);
    };

    const treeSegment = folders.reduce((result = [], folder) => {
      if (
        folder.id === selectedFolderId ||
        recursiveChildCheck(folder, selectedFolderId)
      ) {
        result.push(folder);
      }

      return result;
    }, []);

    return treeSegment;
  };

  const selectedFolderId = "2-1";
  const selectedFolderId1 = "2-2";
  const selectedFolderId2 = "2-4";
  const selectedFolderId3 = "2-3";
  const selectedFolderId4 = "3-1";
  const selectedFolderId5 = "1-1";
  const selectedFolderId6 = "1-2";

  console.log("parent");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId));
  console.log("child");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId1));
  console.log("grandchild"); // this fails
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId2));
  console.log("sibling");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId3));
  console.log("not found");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId4));
  console.log("other parent");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId5));
  console.log("other child");
  console.log(getRelatedTreeFolders(folderTree, selectedFolderId6));

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      {/* <h2>{JSON.stringify(result)}</h2> */}
    </div>
  );
}

标签: javascriptrecursiontree

解决方案


一些问题:

  • recursiveChildCheck应该返回一个布尔值,但在某些情况下不undefined返回 ( ),因为return以下表达式中缺少一条语句:

    folder.children && folder.children.forEach(recursiveChildCheck);
    
  • 此外,在上面的表达式中,运算符的第二个操作数&&永远不会被计算,因为folder.children它是一个数组,并且数组总是真值,即使是空数组。为了给第二个操作数一个机会,第一个操作数应该是folder.children.length > 0

  • 但即使进行了更正,第二个操作数也将始终计算为undefined,因为这是.forEach设计返回的结果。您应该在那里有一个返回布尔值的方法调用,例如some.

  • nestedChildHasIt初始化后永远不会获得任何其他值,因此return true将永远不会发生以下情况:

    if (nestedChildHasIt) return true;
    

    您可能打算nestedChildHasIt在前面的forEach循环中设置为 true,但似乎您在这里有另一种方法来执行与最后的另一个forEach循环相同的操作。

我认为您一直在努力解决的问题是您既需要检查布尔条件(子树是否有id?),并且您需要将子树过滤到正确的子节点,创建一个具有此的新节点独特的孩子。

更正的代码:

function getForestSegment(nodes, id) {
    function recur(nodes) {
        for (const node of nodes) {
            if (node.id === id) return [node];
            const children = recur(node.children);
            if (children.length) return [{ ...node, children}];
        }
        return [];
    }
    return recur(nodes);
}

// Example from question:
const forest = [{id: "1-1",children: [{id: "1-2",parentId: "1-1",children: []}]},{id: "2-1",children: [{id: "2-2",parentId: "2-1",children: [{id: "2-4",parentId: "2-2",children: []}]},{id: "2-3",parentId: "2-1",children: []}]}];

for (const id of ["2-1", "2-2", "2-4", "2-3", "3-1", "1-1", "1-2"]) {
    console.log(id);
    console.log(getForestSegment(forest, id));
}


推荐阅读