JavaScript 实现普通数组数据转化为树形数据结构的步骤说明
在 JavaScript 中,将普通数组数据转化为树形结构的数据是一个常见的任务,特别是在处理层级数据(例如分类、组织结构等)时。下面是一个详细的步骤说明,展示如何将一个扁平的数组转化为树形数据结构。
示例数据
假设我们有以下的扁平数组,每个元素代表一个节点,并且每个节点包含一个 id
和一个 parentId
,parentId
用于表示父节点的关系:
const data = [ { id: 1, name: 'Root', parentId: null }, { id: 2, name: 'Child 1', parentId: 1 }, { id: 3, name: 'Child 2', parentId: 1 }, { id: 4, name: 'Grandchild 1', parentId: 2 }, { id: 5, name: 'Grandchild 2', parentId: 2 }, ];
目标
将上述数据转化为以下树形结构:
[ { id: 1, name: 'Root', children: [ { id: 2, name: 'Child 1', children: [ { id: 4, name: 'Grandchild 1', children: [] }, { id: 5, name: 'Grandchild 2', children: [] } ] }, { id: 3, name: 'Child 2', children: [] } ] } ]
实现步骤
创建一个空对象来存储每个节点的引用:
const nodeMap = {};
遍历数组,初始化节点并填充 nodeMap
:
data.forEach(item => { nodeMap[item.id] = { ...item, children: [] }; });
这样每个节点都被映射到 nodeMap
中,并且每个节点都有一个 children
属性用于存储子节点。
遍历数组,将每个节点添加到其父节点的 children
中:
data.forEach(item => { if (item.parentId) { // 如果节点有父节点,将其添加到父节点的 children 中 const parent = nodeMap[item.parentId]; if (parent) { parent.children.push(nodeMap[item.id]); } } });
提取根节点:
根节点是 parentId
为 null
的节点。我们可以从 nodeMap
中找出这些节点。
const tree = Object.values(nodeMap).filter(node => node.parentId === null);
完整代码示例
以下是将上述步骤整合在一起的完整代码:
const data = [ { id: 1, name: 'Root', parentId: null }, { id: 2, name: 'Child 1', parentId: 1 }, { id: 3, name: 'Child 2', parentId: 1 }, { id: 4, name: 'Grandchild 1', parentId: 2 }, { id: 5, name: 'Grandchild 2', parentId: 2 }, ]; function buildTree(data) { const nodeMap = {}; // 初始化 nodeMap data.forEach(item => { nodeMap[item.id] = { ...item, children: [] }; }); // 构建树形结构 data.forEach(item => { if (item.parentId) { const parent = nodeMap[item.parentId]; if (parent) { parent.children.push(nodeMap[item.id]); } } }); // 提取根节点 return Object.values(nodeMap).filter(node => node.parentId === null); } const tree = buildTree(data); console.log(JSON.stringify(tree, null, 2));
解释
初始化节点映射:
nodeMap
用于存储每个节点的引用,并初始化每个节点的 children
为空数组。
建立父子关系:
遍历数据,找到每个节点的父节点,并将当前节点添加到父节点的 children
中。
提取根节点:
通过过滤 nodeMap
中 parentId
为 null
的节点,得到树的根节点。
递归函数实现
function buildTree(flatData, parentId = null) { return flatData .filter(node => node.parentId === parentId) .map(node => ({ ...node, children: buildTree(flatData, node.id) })); }
参数说明
flatData
: 扁平的数组数据。每个元素包含id
和parentId
,用于表示节点和它们的父节点关系。parentId
: 当前要处理的父节点的 ID。默认为null
,表示初始调用时的根节点。
函数实现细节
过滤节点:
flatData.filter(node => node.parentId === parentId)
filter
方法会返回flatData
中所有parentId
等于当前parentId
的节点。这意味着我们在寻找所有直接子节点。- 初次调用时,
parentId
是null
,所以我们得到的是所有根节点。
映射节点:
.map(node => ({ ...node, children: buildTree(flatData, node.id) }))
map
方法对过滤后的每个节点执行回调函数。- 回调函数的目的是将当前节点的
children
属性设置为一个递归调用buildTree
函数的结果。
递归调用:
buildTree(flatData, node.id)
- 对于每个节点,我们再次调用
buildTree
函数,将当前节点的id
作为新的parentId
。 - 这会找到当前节点的所有子节点,并将这些子节点作为当前节点的
children
属性。
递归过程
初始调用:
buildTree(flatData)
第一次调用时 parentId
是 null
,这意味着我们查找所有根节点。
查找子节点:
对于每一个根节点,buildTree
会被调用,查找该节点的直接子节点。
逐级递归:
对每个子节点,buildTree
会递归调用自身,继续查找该子节点的子节点,直到所有节点的 children
属性都被填充。
示例
假设我们有以下扁平数据:
const data = [ { id: 1, name: 'Root', parentId: null }, { id: 2, name: 'Child 1', parentId: 1 }, { id: 3, name: 'Child 2', parentId: 1 }, { id: 4, name: 'Grandchild 1', parentId: 2 }, { id: 5, name: 'Grandchild 2', parentId: 2 } ];
调用 buildTree(data)
时:
第一层:
parentId
是 null
,找到 ID 为 1
的根节点。
第二层:
对于根节点(ID 为 1
),调用 buildTree(data, 1)
查找其子节点,找到 ID 为 2
和 3
的节点。
第三层:
对于 ID 为 2
的节点,调用 buildTree(data, 2)
查找其子节点,找到 ID 为 4
和 5
的节点。
第四层:
ID 为 4
和 5
的节点没有子节点,所以递归终止,返回空的 children
数组。
最终得到的树形数据结构如下:
[ { id: 1, name: 'Root', children: [ { id: 2, name: 'Child 1', children: [ { id: 4, name: 'Grandchild 1', children: [] }, { id: 5, name: 'Grandchild 2', children: [] } ] }, { id: 3, name: 'Child 2', children: [] } ] } ]
总结
这个 buildTree
函数使用了递归的方式来构建树形数据结构。通过过滤、映射和递归调用,它逐层构建每个节点的子节点,直到所有节点的 children
属性都被正确填充。这种方法简洁且高效,适合处理层级数据。
到此这篇关于JavaScript 实现普通数组数据转化为树形数据结构的文章就介绍到这了,更多相关js普通数组转化树形结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
js提示框替代系统alert,自动关闭alert对话框的实现方法
下面小编就为大家带来一篇js提示框替代系统alert,自动关闭alert对话框的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2016-11-11
最新评论