从表示数据的树中消除根元素

时间:2018-01-10 18:38:29

标签: javascript json higher-order-functions

我有从DB查询的菜单列表,"示例"如下:

[
  { id: 1, name: 'Main Menu', branch: 1, subbranch1: 0, subbranch2: 0 },
  { id: 2, name: 'Main Menu 2', branch: 2, subbranch1: 0, subbranch2: 0 },
  { id: 3, name: 'Sub Menu 1-1', branch: 1, subbranch1: 1, subbranch2: 0 },
  { id: 4, name: 'Sub Menu 1-2', branch: 1, subbranch1: 2, subbranch2: 0 },
  { id: 5, name: 'Sub Menu 1-2-1', branch: 1, subbranch1: 2, subbranch2: 1 },
  { id: 6, name: 'Sub Menu 2-1', branch: 2, subbranch1: 1, subbranch2: 0 },
  { id: 7, name: 'Sub Menu 2-2', branch: 2, subbranch1: 2, subbranch2: 0 },
]

因此,每个菜单都有分支subbranch1 subbranch2值,表示它在菜单列表中的位置。这里的菜单可以是可点击的,或者只是用于分组菜单(拥有其子菜单)的组标题菜单。 例如,id为1的菜单中有3,4作为其子菜单1 另外,id为4的菜单中有5个作为子菜单。

我怎样才能找到所有没有孩子的菜单(或者只是一个可点击的菜单而不是组菜单)。

因此,给出样本的最终结果"应该像

 [
  { id: 3, name: 'Sub Menu 1-1', branch: 1, subbranch1: 1, subbranch2: 0 },
  { id: 5, name: 'Sub Menu 1-2-1', branch: 1, subbranch1: 2, subbranch2: 1 },
  { id: 6, name: 'Sub Menu 2-1', branch: 2, subbranch1: 1, subbranch2: 0 },
  { id: 7, name: 'Sub Menu 2-2', branch: 2, subbranch1: 2, subbranch2: 0 },
]

3 个答案:

答案 0 :(得分:3)

您可以先生成树,然后只选择最深的节点。

  1. 生成树

    • 获取数组中的所有级别键以使其可迭代。
    • 迭代所有节点并为每个找到的嵌套节点构建更深层次的对象。
    • 将节点分配给属性_
  2. 获取最深的节点

    • 获取对象的键。
    • 迭代键。
      • 检查密钥是否为_,然后保留实际的迭代次数。
      • 检查属性的密钥长度是否为1,如果密钥为_,则找到最深的节点。对节点集合采取非常规的属性。离开迭代。
      • 否则尝试从实际密钥中获取最深的节点。
  3. 返回结果。

  4. 
    
    function getDeepestNodes(nodes) {
    
        function getDeepest(node) {
            Object.keys(node).forEach(function (key) {
                if (key === '_') {
                    return;
                }
                if (Object.keys(node[key]).length === 1 && '_' in node[key]) {
                    result.push(node[key]._);
                    return;
                }
                getDeepest(node[key]);
            });
        }
    
        var levels = ['branch', 'subbranch1', 'subbranch2'],
            tree = Object.create(null),
            result = [];
    
        nodes.forEach(function (node) {
            var temp = tree;
            levels.every(function (level, i, ll) {
                temp[node[level]] = temp[node[level]] || {};
                temp = temp[node[level]]
                return node[ll[i + 1]];
            });
            temp._ = node;
        });
    
        getDeepest(tree);
    
        return result;
    }
    
    var data = [{ id: 1, name: 'Main Menu', branch: 1, subbranch1: 0, subbranch2: 0 }, { id: 2, name: 'Main Menu 2', branch: 2, subbranch1: 0, subbranch2: 0 }, { id: 3, name: 'Sub Menu 1-1', branch: 1, subbranch1: 1, subbranch2: 0 }, { id: 4, name: 'Sub Menu 1-2', branch: 1, subbranch1: 2, subbranch2: 0 }, { id: 5, name: 'Sub Menu 1-2-1', branch: 1, subbranch1: 2, subbranch2: 1 }, { id: 6, name: 'Sub Menu 2-1', branch: 2, subbranch1: 1, subbranch2: 0 }, { id: 7, name: 'Sub Menu 2-2', branch: 2, subbranch1: 2, subbranch2: 0 }],
        result = getDeepestNodes(data);
    
    console.log(result);
    
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    
    
    

答案 1 :(得分:1)

这是非常复杂的嵌套循环测试结构。如果每一行都引用了它的直接父菜单的id,那么迭代和测试将是微不足道的。假设这是您描述的菜单结构:

+-------------+
|  Main Menu  |
+-------------+
  +--------------+
  | Sub Menu 1-1 |
  +--------------+
  | Sub Menu 1-2 |
  +--------------+
    +----------------+
    | Sub Menu 1-2-1 |
    +----------------+
+-------------+
| Main Menu 2 |
+-------------+
  +--------------+
  | Sub Menu 2-1 |
  +--------------+
  | Sub Menu 2-2 |
  +--------------+

要解决此问题,您需要执行以下操作:

old=<original array>
new= new array()
for (z=0;z<old.length;z++) // Loop through each line
{
    linkonly = 1 // Using this prevents multiple 
    if (old[z].subbranch2 == 0) // If this is 3rd level, there is not sub menu
    {
        for (y=0;y<old.length;y++) // Loop through each line
        {
            if (z!=y )  // Don't check line against itself
            {
                if (old[z].branch == old[y].branch) // If both of these have the same main branch
                {
                    if (old[z].subbranch1 != 0) // If this is a submenu, continue
                    {
                        if (old[z].subbranch1 == old[y].subbranch1) // They are in the same submenu section
                        {
                            if (old[y].subbranch2 != 0) // This is a submenu of the tested parent
                            {
                                linkonly = 0
                                y=old.length
                            }
                        }
                    }else{
                        if (old[y].subbranch1 != 0) // This is a submenu of the tested item
                            linkonly = 0
                            y=old.length
                        {
                    }
                }
            }
        }
    }
    if (linkonly == 1)
    {
        // push line into new array
    }
}
// use new array

更好的方法是将起始数据结构更改为:

[
  { id: 1, name: 'Main Menu', parent:0, pos:1 },
  { id: 2, name: 'Main Menu 2', parent:0, pos:2 },
  { id: 3, name: 'Sub Menu 1-1', parent:1, pos:1 },
  { id: 4, name: 'Sub Menu 1-2', parent:0, pos:2 },
  { id: 5, name: 'Sub Menu 1-2-1', parent:4, pos:1 },
  { id: 6, name: 'Sub Menu 2-1', parent:2, pos:1 },
  { id: 7, name: 'Sub Menu 2-2', parent:2, pos:2 },
]
然后你可以像这样测试它:

old=<original array>
new= new array()
for (z=0;z<old.length;z++) // Loop through each line
{
    linkonly=1
    for (y=0;y<old.length;y++) // Loop through each line
    {
        if (x!=y) // Don't test against itself
        {
            if (old[y].parent == old[x].id)
            {
                linkonly=0
                y=old.length
            }
        }
    }
    if (linkonly == 1)
    {
        // push line into new array
    }
}
// use new array

您还可以在多维数组中加载这些行,然后只需遍历它们,并通过检查子数组来确定是否存在子菜单。

答案 2 :(得分:0)

&#13;
&#13;
  function isClickableMenu(menu, menus){
    let noOfBranch;
    let noOfSubbranch2;
    if(menu.subbranch1 === 0){
      noOfBranch = menus.reduce((acc,m)=>{
        return m.branch === menu.branch ? acc + 1 : acc;
       },0);
      return noOfBranch === 1;
    }
    if(menu.subbranch2 === 0){
       noOfSubbranch1 = menus.reduce((acc, m)=>{
          return (m.subbranch1 === menu.subbranch1 && m.branch === menu.branch) ? acc + 1 : acc;
       }, 0);
       return noOfSubbranch1 === 1;
    }
    return true
  }

  function getClickableMenus(menus){
    const newArray = [];
    for (let i = 0; i < menus.length; i++) {
      if(isClickableMenu(menus[i], menus)){
       newArray.push(menus[i]);
      }
    }
    return newArray;
  }
  
  const menus = [
    { id: 1, name: 'Main Menu', branch: 1, subbranch1: 0, subbranch2: 0 },
    { id: 2, name: 'Main Menu 2', branch: 2, subbranch1: 0, subbranch2: 0 },
    { id: 3, name: 'Sub Menu 1-1', branch: 1, subbranch1: 1, subbranch2: 0 },
    { id: 4, name: 'Sub Menu 1-2', branch: 1, subbranch1: 2, subbranch2: 0 },
    { id: 5, name: 'Sub Menu 1-2-1', branch: 1, subbranch1: 2, subbranch2: 1 },
    { id: 6, name: 'Sub Menu 2-1', branch: 2, subbranch1: 1, subbranch2: 0 },
    { id: 7, name: 'Sub Menu 2-2', branch: 2, subbranch1: 2, subbranch2: 0 },
  ];
  
  console.log(getClickableMenus(menus));
&#13;
&#13;
&#13;

找到解决方案!..仍然可以有优化空间