简体   繁体   中英

JavaScript: Convert array of objects with parent keys to parent / child tree (including objects with no parent)

I have a list of objects with parent keys that describes several levels of nested parent / child relationships.

const table =[
    {
        "id": 791,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {
        "id": 790,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {
        "id": 845,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {   
        "id": 844,
        "sortOrder": 0,
        "parentCategoryId": 842
    },
    {   
        "id": 802,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {   
        "id": 788,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 863,
        "sortOrder": 0,
        "parentCategoryId": 863
    },
    {    
        "id": 858,
        "sortOrder": 0,
        "parentCategoryId": 858
    },
    {    
        "id": 867,
        "sortOrder": 0,
        "parentCategoryId": 867
    },
    {    
        "id": 871,
        "sortOrder": 0,
        "parentCategoryId": 867
    },
    {    
        "id": 801,
        "name": "Tickets",
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 792,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 797,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 789,
        "name": "Hot food",
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 798,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 671,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 833,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 796,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 843,
        "sortOrder": 0,
        "parentCategoryId": 842
    },
    {    
        "id": 840,
        "sortOrder": 0,
        "parentCategoryId": 793
    },
    {    
        "id": 868,
        "sortOrder": 0,
        "parentCategoryId": 868
    },
    {    
        "id": 851,
        "sortOrder": 0,
        "parentCategoryId": 851
    },
    {    
        "id": 839,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 793,
        "sortOrder": 0,
        "parentCategoryId": 839
    },
    {    
        "id": 859,
        "sortOrder": 0,
        "parentCategoryId": 859
    },
    {   
        "id": 805,
        "sortOrder": 0,
        "parentCategoryId": 859
    },
    {    
        "id": 856,
        "name": "DRINKS",
        "sortOrder": 0,
        "parentCategoryId": 805
    },
    {    
        "id": 870,
        "sortOrder": 0,
        "parentCategoryId": 856
    },
    {    
        "id": 787,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 786,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 799,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 852,
        "sortOrder": 0,
        "parentCategoryId": 852
    },
    {    
        "id": 795,
        "name": "Gents fragrance",
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 864,
        "sortOrder": 0,
        "parentCategoryId": 864
    },
    {   
        "id": 854,
        "sortOrder": 0,
        "parentCategoryId": 854
    },
    {    
        "id": 865,
        "sortOrder": 0,
        "parentCategoryId": 865
    },
    {    
        "id": 869,
        "name": "GFI",
        "sortOrder": 0,
        "parentCategoryId": 869
    },
    {    
        "id": 785,
        "sortOrder": 0,
        "parentCategoryId": 833
    }
]

The issue is I don't have a root parent id with 0. I would like to order this in one array that shows in first level items where the id matches parentCategoryId , what means they are each a root and than each of them to have children within children.

Here is how far I got, but struggle to get this right:

var root = { cid: 0, parent_id: null, children: []};
var node_list = { 0 : root};

     for (var i = 0; i < table.length; i++) {

       console.log('updated list', node_list)
       console.log('item in cat', table[i])

       // check if parent ID exsits in the list
       if (!node_list[table[i].parentCategoryId]) {

         console.log('not in the list');
         console.log('node_list[table[i].parentCategoryId]', table[i].parentCategoryId)
         if (table[i].parentCategoryId === table[i].cid) {
           console.log('it is the root');
           node_list[table[i].cid] = table[i];
         }    

       } else {

         const item = table[i];
         console.log('item is ', item)

         node_list[table[i].parentCategoryId].children = {
           ...node_list[table[i].parentCategoryId].children,
           ...item
         };
       }
     }

Expected result:

const table =[
    {  
        "id": 791,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 790,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 845,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 844,
        "sortOrder": 0,
        "parentCategoryId": 842
    },
    {    
        "id": 802,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 788,
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 863,
        "sortOrder": 0,
        "parentCategoryId": 863
    },
    {    
        "id": 858,
        "sortOrder": 0,
        "parentCategoryId": 858
    },
    {    
        "id": 867,
        "sortOrder": 0,
        "parentCategoryId": 867
    },
    {    
        "id": 871,
        "sortOrder": 0,
        "parentCategoryId": 867
    },
    {    
        "id": 801,
        "sortOrder": 0,
        "parentCategoryId": 847
    },    
    {    
        "id": 797,
        "sortOrder": 0,
        "parentCategoryId": 847,
        children:[
            {    
                "id": 792,
                "sortOrder": 0,
                "parentCategoryId": 797,
                children:[
                    {
                        "id": 671,
                        "sortOrder": 0,
                        "parentCategoryId": 792
                    },
                ]
            },
        ]
    },
    {    
        "id": 789,
        "name": "Hot food",
        "sortOrder": 0,
        "parentCategoryId": 833
    },
    {    
        "id": 798,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {    
        "id": 833,
        "sortOrder": 0,
        "parentCategoryId": 833,
        children:[
            {    
                "id": 785,
                "sortOrder": 0,
                "parentCategoryId": 833
            },
            {
                "id": 786,
                "sortOrder": 0,
                "parentCategoryId": 833
            },
            {
                "id": 787,
                "sortOrder": 0,
                "parentCategoryId": 833
            },
        ]
    },
    {   
        "id": 796,
        "sortOrder": 0,
        "parentCategoryId": 847
    },
    {   
        "id": 843,
        "sortOrder": 0,
        "parentCategoryId": 842
    },
    {   
        "id": 840,
        "sortOrder": 0,
        "parentCategoryId": 793
    },
    {    
        "id": 868,
        "sortOrder": 0,
        "parentCategoryId": 868
    },
    {    
        "id": 851,
        "sortOrder": 0,
        "parentCategoryId": 851
    },
    {    
        "id": 839,
        "sortOrder": 0,
        "parentCategoryId": 847,
        children:[
            {
                "id": 793,
                "sortOrder": 0,
                "parentCategoryId": 839,
                children:[
                    {    
                        "id": 870,
                        "sortOrder": 0,
                        "parentCategoryId": 856
                    },
                ]
            },
        ]
    },
    {    
        "id": 805,
        "sortOrder": 0,
        "parentCategoryId": 859,
        children:[
            {
                "id": 856,
                "sortOrder": 0,
                "parentCategoryId": 805
            },
            {    
                "id": 859,
                "sortOrder": 0,
                "parentCategoryId": 805
            },
        ]
    },      
]

Looks like you want to add a children key with an array of values to the objects in your initial array where the id value corresponds to one or more parentCategoryId values from other objects in the array - and no object should be repeated as a parent or child in the array of nested objects.

You could map the array to append children, and then filter to return just the root parents (and orphans). For example (working snippet below the example if you want to see the output):

const ids = table.map(x => x.id);

let result = table.map((parent) => {
  let children = table.filter((child) => {
    if (child.id !== child.parentCategoryId && child.parentCategoryId === parent.id) {
      return child;
    }
  });
  if (children.length) {
    parent.children = children;
  }
  return parent;
}).filter((obj) => {
  if (obj.id === obj.parentCategoryId || !ids.includes(obj.parentCategoryId)) {
    // include ultimate parents and orphans at root
    return obj;
  }
});

 const table = [{ "id": 791, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 790, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 845, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 844, "sortOrder": 0, "parentCategoryId": 842 }, { "id": 802, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 788, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 863, "sortOrder": 0, "parentCategoryId": 863 }, { "id": 858, "sortOrder": 0, "parentCategoryId": 858 }, { "id": 867, "sortOrder": 0, "parentCategoryId": 867 }, { "id": 871, "sortOrder": 0, "parentCategoryId": 867 }, { "id": 801, "name": "Tickets", "sortOrder": 0, "parentCategoryId": 847 }, { "id": 792, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 797, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 789, "name": "Hot food", "sortOrder": 0, "parentCategoryId": 833 }, { "id": 798, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 671, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 833, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 796, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 843, "sortOrder": 0, "parentCategoryId": 842 }, { "id": 840, "sortOrder": 0, "parentCategoryId": 793 }, { "id": 868, "sortOrder": 0, "parentCategoryId": 868 }, { "id": 851, "sortOrder": 0, "parentCategoryId": 851 }, { "id": 839, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 793, "sortOrder": 0, "parentCategoryId": 839 }, { "id": 859, "sortOrder": 0, "parentCategoryId": 859 }, { "id": 805, "sortOrder": 0, "parentCategoryId": 859 }, { "id": 856, "name": "DRINKS", "sortOrder": 0, "parentCategoryId": 805 }, { "id": 870, "sortOrder": 0, "parentCategoryId": 856 }, { "id": 787, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 786, "sortOrder": 0, "parentCategoryId": 833 }, { "id": 799, "sortOrder": 0, "parentCategoryId": 847 }, { "id": 852, "sortOrder": 0, "parentCategoryId": 852 }, { "id": 795, "name": "Gents fragrance", "sortOrder": 0, "parentCategoryId": 847 }, { "id": 864, "sortOrder": 0, "parentCategoryId": 864 }, { "id": 854, "sortOrder": 0, "parentCategoryId": 854 }, { "id": 865, "sortOrder": 0, "parentCategoryId": 865 }, { "id": 869, "name": "GFI", "sortOrder": 0, "parentCategoryId": 869 }, { "id": 785, "sortOrder": 0, "parentCategoryId": 833 } ]; const ids = table.map(x => x.id); let result = table.map((parent) => { let children = table.filter((child) => { if (child.id !== child.parentCategoryId && child.parentCategoryId === parent.id) { return child; } }); if (children.length) { parent.children = children; } return parent; }).filter((obj) => { if (obj.id === obj.parentCategoryId || !ids.includes(obj.parentCategoryId)) { // include ultimate parents and orphans at root return obj; } }); // stringify just to flatten out SO console result for easier result scanning console.log(JSON.stringify(result));

We have a complex json file that we have to handle with JavaScript to make it hierarchical, in order to later build a tree. Every entry of the JSON array has − id − a unique id, parentId − the id of the parent node (which is 0 if the node is a root of the tree) level − the level of depth in the tree.

The JSON data is already "ordered", means that an entry will have above itself a parent node or brother node, and under itself a child node or a brother node.

 const arr = [   {      "id": "12",      "parentId": "0",      "text": "Man",      "level": "1",      "children": null   },   {      "id": "6",      "parentId": "12",      "text": "Boy",      "level": "2",      "children": null   },   {      "id": "7",      "parentId": "12",      "text": "Other",      "level": "2",      "children": null   },   {      "id": "9",      "parentId": "0",      "text": "Woman",      "level": "1",      "children": null   },   {      "id": "11",      "parentId": "9",      "text": "Girl",      "level": "2",      "children": null   } ]; const listToTree = (arr = []) => {   let map = {}, node, res = [], i;   for (i = 0; i < arr.length; i += 1) {      map[arr[i].id] = i;      arr[i].children = [];   };   for (i = 0; i < arr.length; i += 1) {      node = arr[i];      if (node.parentId !== "0") {         arr[map[node.parentId]].children.push(node);      }      else {         res.push(node);      };   };   return res; }; console.log(JSON.stringify(listToTree(arr), undefined, 4));

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