从父子关系创建菜单路径

Create menu path from parent child relation

给定这个对象数组:

[
  {
    "id": 1942,
    "label": "1",
    "url": "",
    "homepage": false,
    "visible": true,
    "order": 1
  },
  {
    "id": 1943,
    "label": "a",
    "url": "",
    "parentId": 1942,
    "homepage": false,
    "visible": true,
    "order": 1
  },
  {
    "id": 1944,
    "label": "aa",
    "url": "",
    "parentId": 1942,
    "homepage": false,
    "visible": true,
    "order": 2
  },
  {
    "id": 1945,
    "label": "aaa",
    "url": "",
    "parentId": 1942,
    "homepage": false,
    "visible": true,
    "order": 3
  },
  {
    "id": 1946,
    "label": "a1",
    "url": "",
    "parentId": 1943,
    "homepage": false,
    "visible": true,
    "order": 1
  },
  {
    "id": 1947,
    "label": "a2",
    "url": "",
    "parentId": 1943,
    "homepage": false,
    "visible": true,
    "order": 2
  },
  {
    "id": 1948,
    "label": "2",
    "url": "",
    "homepage": false,
    "visible": true,
    "order": 2
  },
  {
    "id": 1949,
    "label": "b",
    "url": "",
    "parentId": 1948,
    "homepage": false,
    "visible": true,
    "order": 1
  },
  {
    "id": 1950,
    "label": "b1",
    "url": "",
    "parentId": 1949,
    "homepage": false,
    "visible": true,
    "order": 1
  },
  {
    "id": 1951,
    "label": "bb",
    "url": "",
    "parentId": 1948,
    "homepage": false,
    "visible": true,
    "order": 2
  },
  {
    "id": 1952,
    "label": "b2",
    "url": "",
    "parentId": 1949,
    "homepage": false,
    "visible": true,
    "order": 2
  }
]

我需要创建一个带有基于父子关系的链接的菜单。

我需要的新数组必须具有以下对象结构。

1

1个/个

1 / aa

1/aaa

1/a/a1

1/a/a2

2

2 / b

2 / bb

2 / b / b1

2 / b / b2

我试过递归但没有成功:

 convert(array) {
    const x = array
      .filter((m, i) => m.id === array[i].parentId)
      .map((menuItem) => ({
        parent: menuItem,
        children: convert(array),
      }));
      console.log(x)
  }

我试过循环,但这种方法只能达到有限的水平,我需要无限的深度。

convert() {
    let parents = array.filter((p) => !p.parentId);
    const children = array.filter((c) => c.parentId);
    let combined = [];
    for (var i = 0; i < parents.length; i++) {
      for (var j = 0; j < children.length; j++) {
        if (children[j].parentId === parents[i].id) {
          combined.push([parents[i], children[j]]);
        }
      }
    }

    console.log(combined);
  }

编辑: 我能够做到 3 层深。 但是对于“无限”级别的递归很深,无法弄清楚。

convert() {
    let parents = this.config.menuItems.filter((p) => !p.parentId);
    const children = this.config.menuItems.filter((c) => c.parentId);
    let combined = [];
    for (var i = 0; i < parents.length; i++) {
      for (var j = 0; j < children.length; j++) {
        if (children[j].parentId === parents[i].id) {
          combined.push([parents[i], children[j]]);
        }
        for (var k = 0; k < children.length; k++) {
          if (children[j].id === children[k].parentId && parents[i].id === children[j].parentId) {
            combined.push([parents[i], children[j], children[k]]);
          }
        }
      }
    }

    console.log(combined);
  }

这是我认为可能有效的递归方法。如果您不想使用 page_children_map,您可以将 page_children_map.get(page.id) 替换为 pages.filter(({ parentId }) => parentId === page.id)

const pages = [{"id":1942,"label":"1","url":"","homepage":false,"visible":true,"order":1},{"id":1943,"label":"a","url":"","parentId":1942,"homepage":false,"visible":true,"order":1},{"id":1944,"label":"aa","url":"","parentId":1942,"homepage":false,"visible":true,"order":2},{"id":1945,"label":"aaa","url":"","parentId":1942,"homepage":false,"visible":true,"order":3},{"id":1946,"label":"a1","url":"","parentId":1943,"homepage":false,"visible":true,"order":1},{"id":1947,"label":"a2","url":"","parentId":1943,"homepage":false,"visible":true,"order":2},{"id":1948,"label":"2","url":"","homepage":false,"visible":true,"order":2},{"id":1949,"label":"b","url":"","parentId":1948,"homepage":false,"visible":true,"order":1},{"id":1950,"label":"b1","url":"","parentId":1949,"homepage":false,"visible":true,"order":1},{"id":1951,"label":"bb","url":"","parentId":1948,"homepage":false,"visible":true,"order":2},{"id":1952,"label":"b2","url":"","parentId":1949,"homepage":false,"visible":true,"order":2}];

const page_children_map = pages.reduce(
  (acc, val) => {
    if(!val.parentId) return acc;
    
    let arr = acc.get(val.parentId);
    if(!arr) acc.set(val.parentId, arr = []);

    arr.push(val);

    return acc;
  },
  new Map()
);

const page_paths = (page, parent_list = []) => {
  let paths = [];
  
  const list = [...parent_list, page];
  const children = page_children_map.get(page.id);
  
  paths.push(list);
  if(children?.length)
    paths.push(...children.flatMap((child) => page_paths(child, list)));
  
  return paths;
};

const res = pages
  .filter(({ parentId }) => !parentId)
  .reduce((acc, val) => {
    acc.push(...page_paths(val));
    return acc;
  }, [])
  .map( (crumbs) => crumbs.map(({ label }) => label).join(' / ') );

console.log(res);

一个相当简单的递归:

const flat = [{"homepage": false, "id": 1942, "label": "1", "order": 1, "url": "", "visible": true}, {"homepage": false, "id": 1943, "label": "a", "order": 1, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1944, "label": "aa", "order": 2, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1945, "label": "aaa", "order": 3, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1946, "label": "a1", "order": 1, "parentId": 1943, "url": "", "visible": true}, {"homepage": false, "id": 1947, "label": "a2", "order": 2, "parentId": 1943, "url": "", "visible": true}, {"homepage": false, "id": 1948, "label": "2", "order": 2, "url": "", "visible": true}, {"homepage": false, "id": 1949, "label": "b", "order": 1, "parentId": 1948, "url": "", "visible": true}, {"homepage": false, "id": 1950, "label": "b1", "order": 1, "parentId": 1949, "url": "", "visible": true}, {"homepage": false, "id": 1951, "label": "bb", "order": 2, "parentId": 1948, "url": "", "visible": true}, {"homepage": false, "id": 1952, "label": "b2", "order": 2, "parentId": 1949, "url": "", "visible": true}]

const nest = (xs, parent = undefined) =>
  xs .filter (({parentId}) => parentId == parent) 
     .map ((p) => ({...p, children: nest (xs, p .id)}))

console .log (nest (flat))
.as-console-wrapper {max-height: 100% !important; top: 0}

这可能创建了一个略有不同版本的对象,与原始对象基本相同,具有嵌套的 children 节点。我发现这是最简单的格式。

但是如果你希望每个级别都有 parentchildren 节点,那么你可以简单地用以下代码替换该函数的最后一行:

     .map ((p) => ({parent: p, children: nest (xs, p .id)}))

如果您对空 children 节点感到困扰,排除它们会稍微复杂一些,但还算不错:

const flat = [{"homepage": false, "id": 1942, "label": "1", "order": 1, "url": "", "visible": true}, {"homepage": false, "id": 1943, "label": "a", "order": 1, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1944, "label": "aa", "order": 2, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1945, "label": "aaa", "order": 3, "parentId": 1942, "url": "", "visible": true}, {"homepage": false, "id": 1946, "label": "a1", "order": 1, "parentId": 1943, "url": "", "visible": true}, {"homepage": false, "id": 1947, "label": "a2", "order": 2, "parentId": 1943, "url": "", "visible": true}, {"homepage": false, "id": 1948, "label": "2", "order": 2, "url": "", "visible": true}, {"homepage": false, "id": 1949, "label": "b", "order": 1, "parentId": 1948, "url": "", "visible": true}, {"homepage": false, "id": 1950, "label": "b1", "order": 1, "parentId": 1949, "url": "", "visible": true}, {"homepage": false, "id": 1951, "label": "bb", "order": 2, "parentId": 1948, "url": "", "visible": true}, {"homepage": false, "id": 1952, "label": "b2", "order": 2, "parentId": 1949, "url": "", "visible": true}]

const nest = (xs, parent = undefined) =>
  xs .filter (({parentId}) => parentId == parent) 
     .map ((p, _, __, kids = nest (xs, p.id)) => ({
       ...p, 
       ...(kids .length ? {children: nest (xs, p.id)} : {})
     }))

console .log (nest (flat))
.as-console-wrapper {max-height: 100% !important; top: 0}