将平面数组转换为嵌套数组

时间:2020-12-31 17:37:19

标签: javascript arrays tree

我有一个这样的对象的javascript数组:

// Id is not necessarily unique, orderly or for any specific purpose
var input = [
    { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
    { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
    { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
    { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
    { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
    { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
    { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
    { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
    { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
    { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
    { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
    //...
];

如何将其转换为这样的内容?

var output = [
    {
        Name: "Europe",
        Children: [
            {
                Name: "Germany",
                Children: [
                    {
                        Name: "Frankfurt",
                        Id: 1,
                        Attribute1: "some attribute"
                    },
                    {
                        Name: "Munich",
                        Id: 2,
                        Attribute1: "some attribute"
                    }
                ]
            },
            {
                Name: "France",
                Children: [
                    {
                        Name: "Paris",
                        Id: 12,
                        Attribute1: "some attribute"
                    },
                    {
                        Name: "Marseille",
                        Id: 14,
                        Attribute1: "some attribute"
                    }
                ]
            }
        ],
        //...
    },
    //...
];

我进行了一些搜索,发现了一些非常相似的主题: Transform array to object tree [JS] array of strings to tree data structure 但我想要的是嵌套数组和对象的组合,而不是上述解决方案中的对象树。

请帮帮我,谢谢!

5 个答案:

答案 0 :(得分:1)

这是使用嵌套 reduce() 调用的解决方案。

const output = input.reduce((a, { LongName, ...attributes }) => {
  const levels = LongName.split(';');
  
  const lastLevel = levels.pop();

  innerChildArray = levels.reduce((b, levelName) => {
    let levelIndex = b.findIndex(({ Name }) => Name === levelName);
    if (levelIndex === -1) {
      levelIndex = b.push({ Name: levelName, Children: [] }) - 1;
    }
    return b[levelIndex].Children;
  }, a);

  innerChildArray.push({ Name: lastLevel, ...attributes })

  return a;
}, []);

console.log(JSON.stringify(output, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
const input = [ { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" }, { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" }, { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" }, { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" }, { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" }, { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" }, { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" }, { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" }, { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" }, { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" }, { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },];
</script>

答案 1 :(得分:0)

<script>
    var input = [
        { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
        { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
        { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
        { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
        { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
        { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
        { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
        { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
        { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
        { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
        { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" }
    ];
    let output = [];
    input.forEach((item) => {
        let names = item.LongName.split(';');
        let records = output.filter((rec) => {
            return rec.Name == names[0];
        });
        let rec = { Name: names[0], Children: [] };
        if (records.length > 0) rec = records[0];
        else output.push(rec);
        let childs = rec.Children.filter((rec) => {
            return rec.Name == names[1];
        });
        let child = { Name: names[1], Children: [] };
        if (childs.length > 0) child = childs[0];
        else rec.Children.push(child);
        child.Children.push({ Name: names[2], Id: item.Id, Attribute1: item.Attribute1 });
    });
    console.log(output);
</script>

output of above program

答案 2 :(得分:0)

使用forEach循环遍历
在每个项目上,用 ;
分割名称 检查要推送的适当数组(父数组或子数组)。 (使用 ptr_arr

const process = (input, output = []) => {
  input.forEach(({LongName, ...rest}) => {
    const keys = LongName.split(";");
    let ptr_arr = output;

    while (keys.length > 0) {
      let key = keys.shift();
      if (keys.length === 0) {
        // its leaf
        ptr_arr.push({ Name: key, ...rest });
      } else {
        let index = ptr_arr.findIndex(({ Name }) => Name === key);
        if (index === -1) {
          ptr_arr.push({ Name: key, Children: [] });
          index = ptr_arr.length - 1;
        }
        ptr_arr = ptr_arr[index].Children;
      }
    }
  });
  return output;
};

var input = [
  { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
  { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
  { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
  { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
  { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
  { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
  { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
  { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
  { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
  { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
  {
    Id: 11,
    LongName: "North America;US;New York",
    Attribute1: "some attribute",
  },
];



console.log(process(input))

答案 3 :(得分:0)

var input = [
    { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
    { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
    { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
    { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
    { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
    { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
    { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
    { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
    { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
    { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
    { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
];

output = input.reduce((result, item) => 
  { 
    let g = item.LongName.split(';'), node1, node2, node3;  

    node1 = result.find         ( ({Name}) => Name === g[0] ); if (node1)
    node2 = node1.Children.find ( ({Name}) => Name === g[1] ); if (node2)
    node3 = node2.Children.find ( ({Name}) => Name === g[2] );

    if (node1 == undefined) result.push ( { Name: g[0], Children: [ { Name: g[1], Children: [ { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } ] } ] } )
    else
    if (node2 == undefined) node1.Children.push ( { Name: g[1], Children: [ { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } ] } )
    else
    if (node3 == undefined) node2.Children.push ( { Name: g[2], Id: item.Id, Attribute1: item.Attribute1 } )
    else
    console.log(item.LongName +' path already exists');
    
    return result;
  }, []
  );
  
  console.log(output);

答案 4 :(得分:0)

这是@pilchards 答案的扩展版本,将处理指定各种级别名称的 LongName 值的情况。

在这个例子中,ids 412 ... 415 是一个 4 级 LongName 嵌套在之前添加的 New York 叶子之下。我还更改了一些变量名称,以更好地说明它们的作用。

// Generic way

var input = [
    { Id: 1, LongName: "Europe;Germany;Frankfurt", Attribute1: "some attribute" },
    { Id: 2, LongName: "Europe;Germany;Munich", Attribute1: "some attribute" },
    { Id: 7, LongName: "Asia;Japan;Okinawa", Attribute1: "some attribute" },
    { Id: 8, LongName: "North America;US;Seattle", Attribute1: "some attribute" },
    { Id: 10, LongName: "Asia;China;Beijing", Attribute1: "some attribute" },
    { Id: 12, LongName: "Europe;France;Paris", Attribute1: "some attribute" },
    { Id: 14, LongName: "Europe;France;Marseille", Attribute1: "some attribute" },
    { Id: 5, LongName: "Asia;Japan;Tokyo", Attribute1: "some attribute" },
    { Id: 6, LongName: "Asia;Korea;Seoul", Attribute1: "some attribute" },
    { Id: 9, LongName: "Asia;Korea;Busan", Attribute1: "some attribute" },
    { Id: 11, LongName: "North America;US;New York", Attribute1: "some attribute" },
    { Id: 412, LongName: "North America;US;New York;County1", Attribute1: "some attribute" },
    { Id: 413, LongName: "North America;US;New York;County2", Attribute1: "some attribute" },
    { Id: 414, LongName: "North America;US;New York;County3", Attribute1: "some attribute" },
    { Id: 415, LongName: "North America;US;New York;County3", Attribute1: "some attribute" },
];

output = input.reduce((rootChildren, { LongName, ...attributes }) => { 
    const levelnames = LongName.split(';');
        const leafname = levelnames.pop();
    
    // descend the tree to the array containing the leafs. insert levels as needed
    const bottomChildren = levelnames.reduce( (children, levelName) => {
    
      let levelIndex = children.findIndex ( ({Name}) => Name === levelName);
      
      if (levelIndex === -1) { // add new level at end of children
        levelIndex = children.push ({ Name: levelName, Children:[] }) - 1;
      } else
      if (!children[levelIndex].hasOwnProperty("Children")) {  // add Children to existing node
        children[levelIndex].Children = [];
      }
      
      return children[levelIndex].Children; // descend
    }, rootChildren);
    
    // add the leaf
    bottomChildren.push({ Name: leafname, ...attributes });
    
    return rootChildren;    
}, []);
  
console.log(JSON.stringify(output,null,2));

相关问题