JavaScript:将具有父键的对象数组转换为父/子树(包括没有父的对象)

Posted

技术标签:

【中文标题】JavaScript:将具有父键的对象数组转换为父/子树(包括没有父的对象)【英文标题】:JavaScript: Convert array of objects with parent keys to parent / child tree (including objects with no parent) 【发布时间】:2019-03-07 13:20:10 【问题描述】:

我有一个带有父键的对象列表,它描述了多个嵌套的父/子关系级别。

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
    
]

问题是我没有 0 的根父 ID。我想在一个数组中排序,该数组显示在第一级项目中,id 匹配 parentCategoryId,这意味着它们每个都是根并且比他们每个人都拥有孩子中的孩子。

这是我已经走了多远,但很难做到这一点:

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
         ;
       
     

预期结果:

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
            ,
        ]
    ,      
]

【问题讨论】:

请同时添加想要的结果。 添加了想要的结果 表和结果不匹配,例如792和797。 我知道,因为我没有时间手动映射这个。但它显示了结构 【参考方案1】:

看起来您想将带有一组值的 children 键添加到初始数组中的对象,其中 id 值对应于数组中其他对象的一个​​或多个 parentCategoryId 值 - 并且没有对象应作为嵌套对象数组中的父对象或子对象重复。

您可以map 数组来附加孩子,然后filter 只返回根父母(和孤儿)。例如(如果您想查看输出,请在示例下方使用 sn-p):

const ids = table.map((x) => x.id);
const result = table.map((parent) => 
  const children = table.filter((child) => 
    if (child.id !== child.parentCategoryId && child.parentCategoryId === parent.id) 
      return true;
    

    return false;
  );

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

  return false;
);

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);
const result = table.map((parent) => 
  const children = table.filter((child) => 
    if (child.id !== child.parentCategoryId && child.parentCategoryId === parent.id) 
      return true;
    
    
    return false;
  );
  
  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 true;
  
  
  return false;
);

// stringify just to flatten out SO console result for easier result scanning
console.log(JSON.stringify(result));

【讨论】:

谢谢@benvc,我看到的唯一问题是该对象在添加为子项后仍在根列表中。所以结果中的元素是重复的。 @ChrisTarasovs 我明白了,在这种情况下,包装函数需要是 filter 而不是 map - 编辑答案以反映澄清。 @ChrisTarasovs - 我改变了从过滤器开始的想法,我认为先映射然后过滤更简单(并且更具可读性) - 相应地编辑了答案。【参考方案2】:

我们有一个复杂的 json 文件,我们必须用 javascript 处理它以使其分层,以便以后构建一棵树。 JSON数组的每个条目都有 - id - 一个唯一的 id, parentId - 父节点的 id(如果节点是树的根,则为 0) level - 树中的深度级别。

JSON 数据已经“有序”,这意味着条目将在其自身之上有一个父节点或兄弟节点,在其自身之下有一个子节点或兄弟节点。

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

【讨论】:

以上是关于JavaScript:将具有父键的对象数组转换为父/子树(包括没有父的对象)的主要内容,如果未能解决你的问题,请参考以下文章

将字符串转换为具有重复键的对象到数组

如何将对象数组转换为在打字稿中具有动态键的单个对象

如何将数组转换为对象并在此对象Javascript中添加相同的键?

将具有相同键的 JSON 对象合并在一起

将通用JS集合转换为具有特定键和分组值的对象

如何计算数组中对象键的总和 - javascript