代码之家  ›  专栏  ›  技术社区  ›  Mario

如何识别节点树中对象的属性中是否存在值?

  •  0
  • Mario  · 技术社区  · 3 年前

    我需要检查的值 url 存在于 path 属性位于以下树的任何对象或节点中。

    关于这棵树。这个 children 属性是可选的,但如果它存在,则可以无限期扩展

    这棵树看起来与下面的相似

    const items = [
      {
        path: "/admin/dashboard",
        menuIcon: {
          icon: "dashboard",
          title: "toolbar.dashboard",
        },
        // children: [
        //   {
        //     path: "/admin/collection",
        //     menuIcon: {
        //       icon: "summarize",
        //       title: "toolbar.reports",
        //     },
        //   },
        // ],
      },
      {
        path: "/admin/contents",
        menuIcon: {
          icon: "archive",
          title: "toolbar.contents",
        },
      },
      {
        path: "/admin/cigar-house-management",
        menuIcon: {
          icon: "home",
          title: "toolbar.directory-management",
        },
        children: [
          {
            path: "/admin/reports",
            menuIcon: {
              icon: "summarize",
              title: "toolbar.reports",
            },
            children: [
              {
                path: "/admin/admin-reports",
                menuIcon: {
                  icon: "summarize",
                  title: "toolbar.reports",
                },
              },
            ],
          },
        ],
      },
    ];
    

    为了达到预期效果,我使用以下功能

    function isValid(url, tree) {
      if (tree && Array.isArray(tree) && tree.length > 0) {
        for (const node of tree) {
          if (node.path === url) {
            return true;
          }
    
          if (!node.children) {
            continue;
          }
    
          const found = isValid(url, node.children);
    
          if (found) {
            return found;
          }
    
          return false;
        }
      }
    }
    

    可以在这里查看

    const url = "/admin/admin-reports";
    const items = [
      {
        path: "/admin/dashboard",
        menuIcon: {
          icon: "dashboard",
          title: "toolbar.dashboard",
        },
        // children: [
        //   {
        //     path: "/admin/collection",
        //     menuIcon: {
        //       icon: "summarize",
        //       title: "toolbar.reports",
        //     },
        //   },
        // ],
      },
      {
        path: "/admin/contents",
        menuIcon: {
          icon: "archive",
          title: "toolbar.contents",
        },
      },
      {
        path: "/admin/cigar-house-management",
        menuIcon: {
          icon: "home",
          title: "toolbar.directory-management",
        },
        children: [
          {
            path: "/admin/reports",
            menuIcon: {
              icon: "summarize",
              title: "toolbar.reports",
            },
            children: [
              {
                path: "/admin/admin-reports",
                menuIcon: {
                  icon: "summarize",
                  title: "toolbar.reports",
                },
              },
            ],
          },
        ],
      },
    ];
    
    function isValid(url, tree) {
      if (tree && Array.isArray(tree) && tree.length > 0) {
        for (const node of tree) {
          if (node.path === url) {
            return true;
          }
    
          if (!node.children) {
            continue;
          }
    
          const found = isValid(url, node.children);
    
          if (found) {
            return found;
          }
    
          return false;
        }
      }
    }
    
    const output = isValid(url, items);
    
    console.log(output);

    但我注意到,当添加一个新节点(如树示例中的注释节点)时,例如作为第一个节点的子节点时,它不会计算父节点之后的节点。

    此外,递归对我来说通常很困难,所以我很感激你的建议。

    提前感谢

    1 回复  |  直到 3 年前
        1
  •  1
  •   Brad    3 年前

    在您的函数中,您将返回 true false 对于具有匹配路径或具有子项的每个条目。使用当前代码继续迭代的唯一方法是不匹配且不具有子代码。

    我建议简化一点,以便更清晰:

    function isValid(url, tree) {
      return tree.some(item =>
        item.path === url || (item.children && isValid(url, item.children))
      );
    }
    
        2
  •  0
  •   zer00ne    3 年前

    “但我注意到,当添加新节点(如树示例中的注释节点)时,例如作为第一个节点的子节点时,它不会评估父节点之后的节点。”

    你是罪魁祸首:

          /* 
          || Without that extra >children< property, the function goes on to find
          || the given search term 
          */ 
          if (!node.children) {
            continue;
          }
          /*
          || But with that extra >children< property on the way the function is 
          || forced to continue onto this snafu. Since the extra >children<
          || property returns as false from isValid(), the control statement 
          || is skipped and goes to the last line which is return false. So the 
          || function terminates because a return statement always terminates the 
          || function thereby never reaching the end of the array. 
          */ 
          const found = isValid(url, node.children);
          if (found) {
            return found;
          }
          /*
          || Do not use a return within a "for" loop. If it is reached then most
          || likely the function terminates too early and the loop never continues 
          || onto the next iteration.
          */
          return false; 
        }
    

    示例中对细节进行了评论

    const admin = [{
        title: "Dashboard",
        path: "/admin/dashboard",
        items: [{
          title: "Dashboard Reports",
          path: "/admin/dashboard/reports"
        }]
      },
      {
        title: "Management",
        path: "/admin/management",
        items: [{
          title: "Management Reports",
          path: "/admin/management/reports"
        }]
      },
      {
        title: "Archive",
        path: "/admin/archive",
        items: [{
          title: "Archive Reports",
          path: "/admin/archive/reports",
          items: [{
            title: "Archive Reports Management",
            path: "/admin/archive/reports/management"
          }, {
            title: "Archive Reports Dashboard",
            path: "/admin/archive/reports/dashboard"
          }]
        }]
      }
    ];
    
    /**
     * Searches for a given string within each object's >path< property value of a
     * given array of objects.
     * @param {String} term - The string segment to search for.
     * @param {Array<object>} struct - The array of objects to search through
     * @returns {Array<array>} - An array of arrays that contain the value of each
     *          >path< property that has >term< within it.
     */
    function searchStruct(term, struct) {
      let data = []; // 1
      if (Array.isArray(struct)) {
        for (const node of struct) {
          if (node.path.includes(term)) {
            data.push(node.path); // 2
          }
          if (node.items) {
            data.push(searchStruct(term, node.items)); // 3
          }
        }
      }
      return data; // 4
    }
    
    console.log(searchStruct("reports", admin));
    /**
     * If you just want a simple true/false returned, replace the line at
     * 1 with: (remove line)
     * 2 with: return true;
     * 3 with: if (searchStruct(term, node.items)) return true;
     * 4 with: return false;
     */  

    要使函数递归,它必须:

    • 声明它将调用自己的条件
      if (node.items) {
        data.push(searchStruct(term, node.items));
      }
      
    • 必须迭代此条件(在循环、数组方法等中)
    • 由递归调用的函数传递一个新值
      searchStruct(term, node.items)
      // New value passed ⤴️