根据父 ID JavaScript 将对象设为子对象 [重复]

Posted

技术标签:

【中文标题】根据父 ID JavaScript 将对象设为子对象 [重复]【英文标题】:Make object as child according to the Parent Id JavaScript [duplicate] 【发布时间】:2019-10-21 18:30:57 【问题描述】:

我有以下来自API的数据:

[
  
    "Code": "01002",
    "ParentAccountId": "01",
  ,
  
    "Code": "01001001003",
    "ParentAccountId": "01001001",
  ,
  
    "Code": "01001004",
    "ParentAccountId": "01001",
  ,
  
    "Code": "02",
    "ParentAccountId": null,
  ,
  
    "Code": "01002001",
    "ParentAccountId": "01002",
  ,
  
    "Code": "02002",
    "ParentAccountId": "02",
  ,
  
    "Code": "02001",
    "ParentAccountId": "02",
  ,
  
    "Code": "01001001001",
    "ParentAccountId": "01001001",
  ,
  
    "Code": "03",
    "ParentAccountId": null,
  ,
  
    "Code": "01002002",
    "ParentAccountId": "01002",
  ,
  
    "Code": "03001",
    "ParentAccountId": "03",
  ,
  
    "Code": "01",
    "ParentAccountId": null,
  ,
  
    "Code": "01001001002",
    "ParentAccountId": "01001001",
  ,
  
    "Code": "01001002",
    "ParentAccountId": "01001",
  ,
  
    "Code": "01001001",
    "ParentAccountId": "01001",
  ,
  
    "Code": "01001003",
    "ParentAccountId": "01001",
  ,
  
    "Code": "01001005",
    "ParentAccountId": "01001",
  ,
  
    "Code": "01001",
    "ParentAccountId": "01",
  
]

看看ParentAccountId

由于我需要将其传递给treeview 组件,因此我需要将其转换为如下内容:

    [
  
    "Code": "01",
    "ParentAccountId": null,
    "children": [
        
            "Code": "01001",
            "ParentAccountId": "01",
            "children": [
                  
                    "Code": "01001001",
                    "ParentAccountId": "01001",
                    "children": [
                        
                            "Code": "01001001001",
                            "ParentAccountId": "01001001",
                            "children": [],
                          ,
                        
                            "Code": "01001001002",
                            "ParentAccountId": "01001001",
                            "children": [],
                          ,
                          
                            "Code": "01001001003",
                            "ParentAccountId": "01001001",
                            "children": [],
                          ,
                    ],
                  ,
                
                    "Code": "01001002",
                    "ParentAccountId": "01001",
                    "children": [],
                  ,
                  
                    "Code": "01001003",
                    "ParentAccountId": "01001",
                    "children": [],
                  ,
                  
                    "Code": "01001004",
                    "ParentAccountId": "01001",
                    "children": [],
                  ,
                  
                    "Code": "01001005",
                    "ParentAccountId": "01001",
                    "children": [],
                  
            ],
          ,
        
            "Code": "01002",
            "ParentAccountId": "01",
            "children": [
                
                    "Code": "01002001",
                    "ParentAccountId": "01002",
                    "children": [],
                  ,
                
                    "Code": "01002002",
                    "ParentAccountId": "01002",
                    "children": [],
                  ,
            ],
          ,
    ],
  ,
  
    "Code": "02",
    "ParentAccountId": null,
    "children": [
          
            "Code": "02001",
            "ParentAccountId": "02",
            "children": [],
          ,
        
            "Code": "02002",
            "ParentAccountId": "02",
            "children": [],
          ,
    ],
  ,
  
    "Code": "03",
    "ParentAccountId": null,
    "children": [
        
            "Code": "03001",
            "ParentAccountId": "03",
            "children": [],
          ,
    ],
  ,
]

我想根据code 将该对象作为其父对象的子对象。该方案是,如果ParentAccountId 为空,则它是***父级,如果ParentAccountId 的长度为2,那么如果ParentAccountId 的长度为5,那么它是第一级子级,那么如果@987654330,它就是第三级子级@ 长度为 8 则为 4 级子级,ParentAccountId 长度为 11 级则为 5 级子级。由于 1 级子级有 2 个长度为 ParentAccountId,那么后续子级将具有 ParentAccountId 作为父级加号的 Code。为了更好地理解,请看第二个,因为我的英语不是那么好。

我对逻辑感到困惑。有什么建议吗?

【问题讨论】:

【参考方案1】:

您可以使用reduce 方法创建树结构来创建递归函数,在每次迭代中检查父 id 是否等于当前元素 id。

const data = ["Id":"1","Code":"01","Title":"Account 01","ParentAccountId":null,"Id":"2","Code":"02","Title":"Account 02","ParentAccountId":null,"Id":"3","Code":"01001","Title":"Account 01001","ParentAccountId":"01","Id":"4","Code":"01002","Title":"Account 01002","ParentAccountId":"01","Id":"5","Code":"01002001","Title":"Account 01002001","ParentAccountId":"01002"]

function toTree(data, pid = null) 
  return data.reduce((r, e) => 
    if (e.ParentAccountId == pid) 
      const obj =  ...e ;
      const children = toTree(data, e.Code);
      if (children.length) obj.children = children;
      r.push(obj);
    
    return r;
  , [])


const result = toTree(data)
console.log(result)

【讨论】:

嘿,@nenad 你的函数更好,并且适用于我的多级数据,你能解释一下它的性能不如使用循环吗?如果没有性能问题,我会选择这个解决方案,否则我会选择循环解决方案,因为我有很多数据。【参考方案2】:

所涉及的逻辑是首先尝试找到每个对象的子对象(使用filter 找到所有具有ParentAccountId 等于每个对象Code 的对象)然后过滤数据以仅返回根父母(ParentAccountId 等于 null 的对象)。

试试下面的代码。

var data = [
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  ,
  
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  ,
  
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  ,
  
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  ,
  
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  
]

rearrangeData = () => 

  var newData = []

  data.forEach((x) => 
    x['children'] = data.filter((y) => 
      return y.ParentAccountId === x.Code
    )
    var parent = data.find((y) => 
      return y.Code === x.ParentAccountId
    )
    if (parent && parent.children) 
      parent.children.push(x)
     else if (parent && !parent.children) 
      parent['children'] = [x];
     else 
      return x
    
    newData.push(parent)
  )

  var parents = newData.filter((x) => 
    return x.ParentAccountId === null
  )

  console.log(parents);


rearrangeData()

【讨论】:

兄弟,它只适用于前两个孩子,检查我已经更新了我代码中的数据,你的代码不适用于该数据。 我编辑了代码 sn-p。现在可以了。您还可以访问 codepen here,因为 sn-p 会切断一些记录的数据。【参考方案3】:

我正在等待工作中的一项任务,所以我想我会为你整理一个实现。并不是说它比链接的线程中的解决方案更好或更差 - 只是另一种实现:

const data = [
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  ,
  
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  ,
  
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  ,
  
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  ,
  
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  
]

function buildTree(obj) 
  // get all top level parents
  let parents = obj.filter((o) => !o.ParentAccountId);
  // loop over the parents and recursively call addChild to populate the tree
  parents.forEach((p) => 
    p.children = addChildren(p, obj);
  );

  return parents;


function addChildren(parent, obj) 
  // find all children for this parent
  let children = obj.filter((o) => o.ParentAccountId === parent.Code)
  if (children.length) 
    // loop over any children recursively calling this function to add nested children
    children.forEach((c) => 
      c.children = addChildren(c, obj);
    );
    return children;
   else 
    return [];
  


console.log(buildTree(data));

【讨论】:

【参考方案4】:

您可以遍历每个节点并构建一个 child-id 到 child 的映射。

然后,再次遍历节点,但这次查看 parent-ids 并将节点推送到父节点(如果它是子节点),或者将其添加到根列表中。此响应改编自 here,并添加了自定义密钥配置。

我还添加了一种方法来删除任何不包含子节点(即叶节点)的对象上的 children 字段。

console.log(convertListToTree(getDataList(), 
  idKey : 'Code',
  parentIdKey : 'ParentAccountId',
  pruneEmptyChildren : true
));

function convertListToTree(list, options) 
  options = Object.assign(
    idKey : 'id',
    parentIdKey : 'parentId',
    childrenKey : 'children',
    pruneEmptyChildren : false
  , options || );
  let map = , node, roots = [], i;
  for (i = 0; i < list.length; i++) 
    map[list[i][options.idKey]] = i;
    list[i][options.childrenKey] = []; // Attach a "child" reference holder
  
  for (i = 0; i < list.length; i++) 
    node = list[i];
    if (node[options.parentIdKey] != null) 
      list[map[node[options.parentIdKey]]][options.childrenKey].push(node);
     else 
      roots.push(node);
    
  
  if (options.pruneEmptyChildren) 
    pruneEmptyKeys(roots, options.childrenKey); // Remove empty
  
  return roots;


function pruneEmptyKeys(tree, childKey) 
  let items = tree[childKey] || tree;
  items.forEach(item => 
    if (item[childKey].length > 0) 
      pruneEmptyKeys(item[childKey], childKey);
     else 
      delete item[childKey]; // Remove empty child list
    
  );


function getDataList() 
  return [
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  , 
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  , 
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  , 
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  , 
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  ];
.as-console-wrapper 
  top: 0;
  max-height: 100% !important;
&lt;!-- Adapted from: https://***.com/a/18018037/1762224 --&gt;

【讨论】:

以上是关于根据父 ID JavaScript 将对象设为子对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何根据自己的宽度而不是父 div 将 CSS 边距/填充设置为子 div?

JavaScript学习手册(58)

JavaScript高级之继承

使用 .htaccess 将 Web 根文件夹设为子文件夹?

使用休眠 jpa 进行 JSON 序列化和反序列化以在 JSON 响应中将父对象转换为子对象

JavaScript里面向对象的继承:构造函数"继承"的五种方法