[英]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>
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.