简体   繁体   中英

Creating nested json object based on given condition

I have a flat list (array of objects), like next one:

var myList = [
  {id:1, name:"ABC", type:"level_1"},
  {id:2, name:"XYZ", type:"level_1"},
  {id:1, name:"ABC_level 2", type:"level_2", level_one_id:1},
  {id:2, name:"XYZ_level 2", type:"level_2", level_one_id:2},
  {id:1, name:"ABC_level 3", type:"level_3", level_two_id:1},
  {id:2, name:"XYZ_level 3", type:"level_3", level_two_id:2},
];

Then, I have to group them in such a way that I can create a hierarchy of levels (which I tried to do in the below lines of code):

 var myList = [ {id:1, name:"ABC", type:"level_1"}, {id:2, name:"XYZ", type:"level_1"}, {id:1, name:"ABC_level 2", type:"level_2", level_one_id:1}, {id:2, name:"XYZ_level 2", type:"level_2", level_one_id:2}, {id:1, name:"ABC_level 3", type:"level_3", level_two_id:1}, {id:2, name:"XYZ_level 3", type:"level_3", level_two_id:2}, ]; var myNestedList = { levels: [] }; //-----------pushing level1---------- myList.forEach((res => { if (res.type == "level_1") { myNestedList.levels.push(res); } })); //-----------pushing level 2--------- myNestedList.levels.forEach((res) => { myList.forEach((val) => { if (val.type == "level_2" && val.level_one_id == res.id) { res["level_2"] = [] || res["level_2"]; res["level_2"].push(val); } }) }) //-----------pushing level 3--------- myNestedList.levels.forEach((res) => { res["level_2"].forEach((val) => { myList.forEach((lastlevel) => { if (lastlevel.type == "level_3" && lastlevel.level_two_id == val.id) { val["level_3"] = [] || val["level_3"]; val["level_3"].push(lastlevel); } }) }) }) console.log(myNestedList);

Although I'm able to achieve the result, I'm sure this code can be more precise and meaningful. Can we make use of lodash here and get this code shorter?

Any help would be much appreciated. Thanks!

You could take a virtual unique id for the object and for referencing the parents and collect the items in a tree.

This approach works with unsorted data as well.

 var data = [{ id: 1, name: "ABC", type: "level_1" }, { id: 2, name: "XYZ", type: "level_1" }, { id: 1, name: "ABC_level 2", type: "level_2", level_one_id: 1 }, { id: 2, name: "XYZ_level 2", type: "level_2", level_one_id: 2 }, { id: 1, name: "ABC_level 3", type: "level_3", level_two_id: 1 }, { id: 2, name: "XYZ_level 3", type: "level_3", level_two_id: 2 }], tree = function (data) { var t = {}; data.forEach(o => { var level = o.type.match(/\\d+$/)[0], parent = o[Object.keys(o).filter(k => k.startsWith('level_'))[0]] || 0, parentId = `${level - 1}.${parent}`, id = `${level}.${o.id}`, children = `level_${level}`; Object.assign(t[id] = t[id] || {}, o); t[parentId] = t[parentId] || {}; t[parentId][children] = t[parentId][children] || []; t[parentId][children].push(t[id]); }); return t['0.0'].level_1; }(data); console.log(tree);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

I can't make any sense of this data representing a real tree. But I can see it turning into something like a list of lists, one for each base id, something like this:

[
  [
    {id: 1, name: "ABC", type: "level_1"},
    {id: 1, name: "ABC_level 2", type: "level_2", level_one_id: 1},
    {id: 1, name: "ABC_level 3", type: "level_3", level_two_id: 1}
  ],
  [
    {id: 2, name: "XYZ", type: "level_1"},
    {id: 2, name: "XYZ_level 2", type: "level_2", level_one_id: 2},
    {id: 2, name: "XYZ_level 3", type: "level_3", level_two_id: 2}
  ]
]

If that format is useful, then this code could help you get there:

 // utility function const group = (fn) => (xs) => Object .values (xs .reduce ((a, x) => ({...a, [fn (x)]: (a [fn (x)] || []) .concat (x)}), {})) // helper function const numericSuffix = str => Number (str .type .match (/(\\d+)$/) [1]) // main function -- I don't have a sense of what a good name for this would be const foo = (xs) => group (o => o.id) (xs) .map (x => x .sort ((a, b) => numericSuffix(a) - numericSuffix(b))) // data const myList = [{id: 1, name: "ABC", type: "level_1"}, {id: 2, name: "XYZ", type: "level_1"}, {id: 1, name: "ABC_level 2", type: "level_2", level_one_id: 1}, {id: 2, name: "XYZ_level 2", type: "level_2", level_one_id: 2}, {id: 1, name: "ABC_level 3", type: "level_3", level_two_id: 1}, {id: 2, name: "XYZ_level 3", type: "level_3", level_two_id: 2}] // demo console .log (foo (myList))
 .as-console-wrapper {max-height: 100% !important; top: 0}

We use a custom group function as well as one that extracts the numeric end of a string (to be used in sorting so that level_10 comes after level_9 and not before level_2 ) group could be replaced by Underscore, lodash or Ramda groupBy functions, but you'd probably then have to call Object.values() on the results.

The main function groups the data on their ids, then sorts the group by that numeric suffix.

Note that this technique only makes sense if there is only one element for a given id at any particular level. If there could be more, and you really need a tree, I don't see how your input structure could determine future nesting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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