根据id递归获取树段

Recursively get segment of tree according to id

我正在尝试根据 id 获取树段。 id 可能位于根目录中,也可能位于 children 中的任何位置。我的目标是获取整个家谱线,而不是其他 non-related 数据。

我有整个数据树,还有一个 id。

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

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>
  );
}

一些问题:

  • recursiveChildCheck 应该是 return 一个布尔值,但在某些情况下,没有任何内容 (undefined) 是 returned,因为 return 语句在以下表达式中丢失:

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

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

  • nestedChildHasIt 在初始化后永远不会得到任何其他值,因此下面的 return true 永远不会发生:

    if (nestedChildHasIt) return true;
    

    您可能打算在前面的 forEach 循环中将 nestedChildHasIt 设置为 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));
}