假设我的对象也有一个数组:
[
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts",
"parentId": "5c7bf2191d41c810b2ad6186",
"childrenIds": []
},
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"parentId": null,
"childrenIds": [
"5a97e047f826a0111b754beb"
]
}
]
我想做的是一个函数,该函数返回另一个数组,但是这次只包含没有将parentID作为根元素,并且在其上具有包含其子代的子代数组的项目,因此到达有一个空的childrenIDs数组。 (还要删除父/子id属性)
对于先前的输入,我将返回类似的内容
[
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"children": [
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts"
}
]
}
]
我似乎无法想到该任务的任何有效代码,任何人都可以帮忙吗?
您可以通过对象中的ID保留对每个节点的引用,并在运行时构建树。由于我们可能会遇到对我们尚未看到的条目的引用,因此我们将在此期间创建存根(仅由子数组组成),并在以后将其替换为实际的存根。
似乎您有一个垂直的双向链接列表,既将父级ID保存在子级中,又将子级ID保存在父级中,但是我们只需要其中一个即可构建树。我们将使用存储在每个孩子中的父母ID。 (请注意,这是假设您的结构是一致的,没有不平衡的关系或悬挂的引用。)
为简单起见,我们创建一个根节点,最后将返回其子节点,这样我们就不必在没有父节点的情况下处理节点。
然后您可以编写如下代码:
function makeTree (rows) {
// Use a symbol to denote the root ID to avoid clashes with any real IDs
const ROOT = Symbol('ROOT')
// Node index, by ID
// Add a root node from the start
const nodes = { [ROOT]: { children: [] } }
// Helper function to return an existing node or create a stub if not existing
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = { children: [] })
for (const row of rows) {
// Add current row to index, merging data with existing stub, if any.
// This keeps any existing references in other nodes' children arrays intact
// because Object.assign mutates the first object passed to it and returns the
// same object.
const node = Object.assign(getNodeOrCreateStub(row.id), row)
// Remove unwanted properties.
delete node.parentId
delete node.childrenIds
// Get parent or create parent stub if parent node not already existing
const parent = getNodeOrCreateStub(row.parentId || ROOT)
// Add current node as child to parent
parent.children.push(node)
}
// Return children of root node
return nodes[ROOT].children
}
请注意,此代码当前还在叶节点中创建空的children
数组,这与上面的示例不同。但是我认为这使代码更简单,因为它不必处理叶节点,而不必创建树或以后读取它! (您不必做children && children.length
检查孩子,您总是可以直接访问children
。)