按 Object 从数组 Object 到树 Object Javascript 分组

Group by Object from Array Object to Tree Object Javascript

我需要像这里这样的帮助组 object: 输入数组为:

[
    {
        Id: '1234',
        Name: 'Country - Viet Nam',
        AdminLevel: 2
    },
    {
        Id:'5678',
        Name: 'Province - Ho Chi Minh',
        AdminLevel: 4,
        ParentId: '1234'
    },
    {
        Id:'91011',
        Name: 'Province - Ha Noi',
        AdminLevel: 4,
        ParentId: '1234'
    },
    {
        Id:'111213',
        Name: 'Province - Da Nang',
        AdminLevel: 4,
        ParentId: '1234'
    },
    {
        Id:'111213',
        Name: 'District - Quan 1',
        AdminLevel: 6,
        ParentId: '5678'
    },
    {
        Id:'111213',
        Name: 'District - Quan 2',
        AdminLevel: 6,
        ParentId: '5678'
    },
    {
        Id:'111213',
        Name: 'District - Quan 3',
        AdminLevel: 6,
        ParentId: '5678'
    },
    {
        Id:'111213',
        Name: 'District - Tinh nao do Ha Noi 1',
        AdminLevel: 6,
        ParentId: '91011'
    },
    {
        Id:'111213',
        Name: 'District - Tinh nao do Ha Noi 2',
        AdminLevel: 6,
        ParentId: '91011'
    },
    {
        Id:'111213',
        Name: 'District - Tinh nao do Ha Noi 3',
        AdminLevel: 6,
        ParentId: '91011'
    }
    
]

并且期望 out-put 是:

{
    Id: '1234',
    Name: 'Country - Viet Nam',
    AdminLevel: 2,
    Data: [
        {
            Id:'5678',
            Name: 'Province - Ho Chi Minh',
            AdminLevel: 4,
            ParentId: '1234',
            Data: [
                {
                    Id:'111213',
                    Name: 'District - Quan 1',
                    AdminLevel: 6,
                    ParentId: '5678'
                },
                {
                    Id:'111213',
                    Name: 'District - Quan 2',
                    AdminLevel: 6,
                    ParentId: '5678'
                },
                {
                    Id:'111213',
                    Name: 'District - Quan 3',
                    AdminLevel: 6,
                    ParentId: '5678'
                }
            ]
        },
        {
            Id:'91011',
            Name: 'Province - Ha Noi',
            AdminLevel: 4,
            ParentId: '1234',
            Data: [
                {
                    Id:'111213',
                    Name: 'District - Tinh nao do Ha Noi 1',
                    AdminLevel: 6,
                    ParentId: '91011'
                },
                {
                    Id:'111213',
                    Name: 'District - Tinh nao do Ha Noi 2',
                    AdminLevel: 6,
                    ParentId: '91011'
                },
                {
                    Id:'111213',
                    Name: 'District - Tinh nao do Ha Noi 3',
                    AdminLevel: 6,
                    ParentId: '91011'
                }
            ]
        },
        {
            Id:'111213',
            Name: 'Province - Da Nang',
            AdminLevel: 4,
            ParentId: '1234'
        },
    ]
}

说明:我们要先按AdminLevel分组,再按ParentId分组。

假设您的数据已排序,因此 parents 在 children 之前,这很简单:

  1. 遍历所有节点
  2. 对于每个找到 parent
  3. 添加到 parent 的 Data 属性(如果还没有则先创建)
  4. Return根节点

根节点将是没有任何 parent 的节点。您可以使用地图来跟踪访问过的项目并使查找 parent 更容易。

由于您的数据没有唯一 ID,因此您需要为每个 ID 保留一个项目数组。如果您的数据恰好有一个 parent ID 匹配两个或更多的东西,那么每个东西都会有 child 节点。

const data = [ { Id: '1234', Name: 'Country - Viet Nam', AdminLevel: 2 }, { Id:'5678', Name: 'Province - Ho Chi Minh', AdminLevel: 4, ParentId: '1234' }, { Id:'91011', Name: 'Province - Ha Noi', AdminLevel: 4, ParentId: '1234' }, { Id:'111213', Name: 'Province - Da Nang', AdminLevel: 4, ParentId: '1234' }, { Id:'111213', Name: 'District - Quan 1', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Quan 2', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Quan 3', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 1', AdminLevel: 6, ParentId: '91011' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 2', AdminLevel: 6, ParentId: '91011' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 3', AdminLevel: 6, ParentId: '91011' } ];

function group(arr) {
  let root;
  
  //keep a reference of all elements visited
  const lookup = new Map();
  
  //go through the array
  for (const item of arr) {
    //add to the lookup
    const key = item.Id;
    const value = lookup.get(key) ?? [];
    lookup.set(key, value.concat(item));
    
    if ("ParentId" in item){
      //find the parent(s) and update
      lookup.get(item.ParentId)
        .forEach(parent => {
          parent.Data = parent.Data ?? [];
          parent.Data.push(item);
        });
    } else {
      //if the item doesn't have a parent, it's the root node
      root = item;
    }
  }
  
  return root;
}

const result = group(data);

console.log(result);

如果您的数据未排序并且 parents 可能出现在它们的 children 之前或之后,以上代码将不起作用,因为它只访问并找到 parents 的顺序,例如,它将因 [{Id: 2, ParentId: 1}, {Id: 1}] 而失败。如果恰好是这种情况,您可以更改函数的主体以首先索引所有项目,然后 然后 将它们添加到各自的 parents:

const data = [ { Id: '1234', Name: 'Country - Viet Nam', AdminLevel: 2 }, { Id:'5678', Name: 'Province - Ho Chi Minh', AdminLevel: 4, ParentId: '1234' }, { Id:'91011', Name: 'Province - Ha Noi', AdminLevel: 4, ParentId: '1234' }, { Id:'111213', Name: 'Province - Da Nang', AdminLevel: 4, ParentId: '1234' }, { Id:'111213', Name: 'District - Quan 1', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Quan 2', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Quan 3', AdminLevel: 6, ParentId: '5678' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 1', AdminLevel: 6, ParentId: '91011' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 2', AdminLevel: 6, ParentId: '91011' }, { Id:'111213', Name: 'District - Tinh nao do Ha Noi 3', AdminLevel: 6, ParentId: '91011' } ];

function group(arr) {
  let root;
  
  //keep a reference of all elements visited
  const lookup = new Map();
  
  //first index all items
  for (const item of arr) {
     //add to the lookup
    const key = item.Id;
    const value = lookup.get(key) ?? [];
    lookup.set(key, value.concat(item));
  }
  
  //go through the array
  for (const item of arr) {
    if ("ParentId" in item){
      //find the parent(s) and update
      lookup.get(item.ParentId)
        .forEach(parent => {
          parent.Data = parent.Data ?? [];
          parent.Data.push(item);
        });
    } else {
      //if the item doesn't have a parent, it's the root node
      root = item;
    }
  }
  
  return root;
}

const result = group(data);

console.log(result);

这仍然是一个 O(n) 解决方案,因为它将随着输入线性增长。

请注意,这不是按 AdminLevel 分组,因为似乎没有必要。数据自然根据 ParentId 进行分组。首先按 AdminLevel 分组是一项无关的操作,因为您不希望每个 parent.

存在多个组

a TypeScript solution using the same algorithm courtesy of jcalz