简体   繁体   English

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

[英]convert flat array to nested array

I have a javascript array of objects like this:我有一个 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" },
    //...
];

How do I convert it to something like this?我如何将它转换成这样的东西?

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"
                    }
                ]
            }
        ],
        //...
    },
    //...
];

I did some searching and found some very similar topics: Transform array to object tree [JS] array of strings to tree data structure But what I want is a combination of nested arrays and objects, instead of a tree of objects from the above solutions.我做了一些搜索,发现了一些非常相似的主题: 将数组转换为 object 树 [JS] 字符串数组到树数据结构但我想要的是嵌套 arrays 和对象的组合,而不是上述解决方案中的对象树。

Please help me, thanks!请帮助我,谢谢!

Here is a solution using nested reduce() calls.这是使用嵌套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>

<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上述程序的output

Use forEach loop to traverse使用forEach循环遍历
On each item, split the name by ;在每个项目上,将名称拆分为;
Check appropriate array (either parent or children array) to push.检查要推送的适当数组(父数组或子数组)。 (using the ptr_arr ) (使用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))

 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);

This is an extended version of @pilchards answer that will handle the case of the LongName values specifying a variety of level names.这是@pilchards 答案的扩展版本,它将处理LongName值指定各种级别名称的情况。

In this example the ids 412... 415 are a 4 level LongName nesting below an earlier added New York leaf.在此示例中,id 412... 415是嵌套在较早添加New York叶下方的 4 级LongName I also changed some of the variable names to be more indicative of their role.我还更改了一些变量名称以更能说明它们的作用。

 // 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));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM