通过键对对象数组进行分组。如何处理任何深度的后代?

Grouping array of objects items by keys. How to handle any depth of decendants?

给定一个平面对象数组,我按 pid(父 ID)将子项分组到父项的 ID。我的函数正是这样做的,但只有两层深。我怎样才能把这个函数变成一个处理任何深度嵌套的函数?

这是数据:

const list = 
 [   
     { "pid": 0, "id": "solo"},
     { "pid": 0, "id": "member"},
     { "pid": 0, "id": "solo2"},
     { "pid": "member", "id": "10_admin_members"},
     { "pid": "member", "id": "10_invitations"},
     { "pid": "member", "id": "sub_member"},
     { "pid": "sub_member", "id": "other"},
     { "pid": "sub_member", "id": "other2"},
     { "pid": 0, "id": "admin"},
     { "pid": "admin", "id": "admin_general"},
     { "pid": "admin", "id": "admin_modules"},
     { "pid": "admin", "id": "admin_roles"},
     { "pid": "admin", "id": "admin_navigation"},
 ]

以及函数:

const groupChildrenToParentsByKeys = (firstLevelItems, groupedItems, childArrayKey = 'items') 
 => {
  let cleanArray = []
  for (const a in firstLevelItems) {
      let childrenCount = 0
      for (const b in groupedItems) {
          if(firstLevelItems[a].id == b) {
              let descendantsArray = []
              let childrenArray = groupedItems[b]

                for (const c in childrenArray) {
                  let grandChildrenCount = 0
                  for (const h in groupedItems) {
                    if(childrenArray[c].id == h) {
                      let grandChildrenArray = groupedItems[h]
                      descendantsArray.push({...childrenArray[c], [childArrayKey]: grandChildrenArray})
                      grandChildrenCount ++
                    } 
                  }
                  if(!grandChildrenCount) {
                    descendantsArray.push(childrenArray[c])
                  }
                }
              cleanArray.push({...firstLevelItems[a], [childArrayKey]: descendantsArray})
              childrenCount ++
          }
      } 
      if(!childrenCount) {
        cleanArray.push(firstLevelItems[a])
      }
  }
  return cleanArray

}

const list = 
 [   
     { "pid": 0, "id": "solo"},
     { "pid": 0, "id": "member"},
     { "pid": 0, "id": "solo2"},
     { "pid": "member", "id": "10_admin_members"},
     { "pid": "member", "id": "10_invitations"},
     { "pid": "member", "id": "sub_member"},
     { "pid": "sub_member", "id": "other"},
     { "pid": "sub_member", "id": "other2"},
     { "pid": 0, "id": "admin"},
     { "pid": "admin", "id": "admin_general"},
     { "pid": "admin", "id": "admin_modules"},
     { "pid": "admin", "id": "admin_roles"},
     { "pid": "admin", "id": "admin_navigation"},
 ]

// Matches two objects by keys and inserts the children inside of parents as array
const groupChildrenToParentsByKeys = (firstLevelItems, groupedItems, childArrayKey = 'items') => {
    let cleanArray = []
    for (const a in firstLevelItems) {
        let childrenCount = 0
        for (const b in groupedItems) {
            if(firstLevelItems[a].id == b) {
                let descendantsArray = []
                let childrenArray = groupedItems[b]
                
                  for (const c in childrenArray) {
                    let grandChildrenCount = 0
                    for (const h in groupedItems) {
                      if(childrenArray[c].id == h) {
                        let grandChildrenArray = groupedItems[h]
                        descendantsArray.push({...childrenArray[c], [childArrayKey]: grandChildrenArray})
                        grandChildrenCount ++
                      } 
                    }
                    if(!grandChildrenCount) {
                      descendantsArray.push(childrenArray[c])
                    }
                  }
                cleanArray.push({...firstLevelItems[a], [childArrayKey]: descendantsArray})
                childrenCount ++
            }
        } 
        if(!childrenCount) {
          cleanArray.push(firstLevelItems[a])
        }
    }
    return cleanArray
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let groupedObject = _.groupBy(list, 'pid') 
let firstLevel = {...groupedObject}[0]
let childrenParentArray = groupChildrenToParentsByKeys(firstLevel, groupedObject)
console.log(JSON.stringify(childrenParentArray, null, 2))
console.log(_.size(childrenParentArray))
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

进行一次循环,同时为 parent/children 和 children/parent 创建一个关系。结果,您得到一棵依赖于关系而不是数据顺序的树。

const
    getTree = (data, root) => {
    const t = {};
        data.forEach(o => 
            ((t[o.pid] ??= {}).items ??= []).push(Object.assign(t[o.id] ??= {}, o))
        );
        return t[root].items;
    },
    list = [{ pid: 0, id: "solo" }, { pid: 0, id: "member" }, { pid: 0, id: "solo2" }, { pid: "member", id: "10_admin_members" }, { pid: "member", id: "10_invitations" }, { pid: "member", id: "sub_member" }, { pid: "sub_member", id: "other" }, { pid: "sub_member", id: "other2" }, { pid: 0, id: "admin" }, { pid: "admin", id: "admin_general" }, { pid: "admin", id: "admin_modules" }, { pid: "admin", id: "admin_roles" }, { pid: "admin", id: "admin_navigation" }],
    tree = getTree(list, 0);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }