从数组对象到树对象Javascript按对象分组

问题描述

我需要像这样的帮助组对象: 输入数组为:

[
    {
        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',{
        Id:'111213',Name: 'Province - Da Nang',Name: 'district - Quan 1',AdminLevel: 6,ParentId: '5678'
    },Name: 'district - Quan 2',Name: 'district - Quan 3',Name: 'district - Tinh nao do Ha Noi 1',ParentId: '91011'
    },Name: 'district - Tinh nao do Ha Noi 2',Name: 'district - Tinh nao do Ha Noi 3',ParentId: '91011'
    }
    
]

预计产量为:

{
    Id: '1234',AdminLevel: 2,Data: [
        {
            Id:'5678',ParentId: '1234',Data: [
                {
                    Id:'111213',ParentId: '5678'
                },{
                    Id:'111213',ParentId: '5678'
                }
            ]
        },{
            Id:'91011',ParentId: '91011'
                },ParentId: '91011'
                }
            ]
        },{
            Id:'111213',ParentId: '1234'
        },]
}

说明:我们必须先按AdminLevel分组,然后再按ParentId分组。

  • 别忘了我们具有以下结构:国家(父母)-省(多个-国家的孩子)-地区(多个-省的孩子)。 非常感谢您对我的帮助

解决方法

假设您的数据是有序的,那么父母先于孩子,这很简单:

  1. 遍历所有节点
  2. 为每个找到父母的人
  3. 添加到父级的Data属性(如果尚不存在,请先创建)
  4. 返回根节点

根节点将是没有任何父节点的节点。您可以使用地图来跟踪访问的物品,并使其更容易查找父母。

由于数据没有唯一的ID,因此您需要为每个ID保留一组项目。如果您的数据碰巧具有与两个或多个事物匹配的父ID,则每个数据都将具有子节点。

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',{ Id:'111213',Name: 'Province - Da Nang',Name: 'District - Quan 1',AdminLevel: 6,ParentId: '5678' },Name: 'District - Quan 2',Name: 'District - Quan 3',Name: 'District - Tinh nao do Ha Noi 1',ParentId: '91011' },Name: 'District - Tinh nao do Ha Noi 2',Name: 'District - Tinh nao do Ha Noi 3',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);

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

const data = [ { Id: '1234',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进行分组是一项无关紧要的操作。

使用与a TypeScript solution相同的算法查看jcalz