JavaScript 构造树形结构的一种高效算法
引言
我们经常会碰到树形数据结构,比如组织层级、省市县或者动植物分类等等数据。下面是一个树形结构的例子:
在实际应用中,比较常见的做法是将这些信息存储为下面的结构,特别是当存在1对多的父/子节点关系时:
const data = [
{ id: 56, parentId: 62 },
{ id: 81, parentId: 80 },
{ id: 74, parentId: null },
{ id: 76, parentId: 80 },
{ id: 63, parentId: 62 },
{ id: 80, parentId: 86 },
{ id: 87, parentId: 86 },
{ id: 62, parentId: 74 },
{ id: 86, parentId: 74 },
];
那么,如何将这种对象数组格式转换为层级树的格式呢?其实,利用 JavaScript 对象引用的特性,实现起来会非常简单。它可以不用递归,在O(n)时间内完成。
术语
为了表述方便,我们先来定义几个术语。我们把数组中的每个元素(也就树形图里的每个圆圈)称为“节点”。节点可以是多个节点的“父节点”,也可以是某个节点的“子节点”。上图中,节点 86
是节点 80
和节点 87
的“父节点”,节点 86
是节点 74
的子节点。树的最顶部节点称为“根节点”。
思路
为了构造树形结构,我们需要:
- 遍历
data
数组 - 找到当前元素的父元素
- 在父元素对象上添加一个对该子元素的引用
- 元素如果没有父元素,那我们就认为它是树的根节点
我们可以看到到,引用被保存在对象树下,这就是为什么我们可以在O(n)时间内完成这个任务!
建立 ID-数组索引映射关系
虽然不是必需的,但是这个映射关系可以帮我们快速找到元素的位置,方便找到到父元素的引用。
const idMapping = data.reduce((acc, el, i) => {
acc[el.id] = i;
return acc;
}, {});
映射结果如下,后面你会看到它的用处有多大:
{
56: 0,
62: 7,
63: 4,
74: 2,
76: 3,
80: 5,
81: 1,
86: 8,
87: 6,
};
构造树形结构
现在我们开始构造这个树形结构。遍历这个对象数组,找到每个元素的父元素对象,然后添加对这个元素的引用。现在你应该看到了,这个 idMapping
用来定位元素的位置多么方便(常数时间)。
let root;
data.forEach(el => {
// 判断根节点
if (el.parentId === null) {
root = el;
return;
}
// 用映射表找到父元素
const parentEl = data[idMapping[el.parentId]];
// 把当前元素添加到父元素的`children`数组中
parentEl.children = [...(parentEl.children || []), el];
});
完事!用console.log
打印 root
看下:
console.log(root);
{
id: 74,
parentId: null,
children: [
{
id: 62,
parentId: 74,
children: [{ id: 56, parentId: 62 }, { id: 63, parentId: 62 }],
},
{
id: 86,
parentId: 74,
children: [
{
id: 80,
parentId: 86,
children: [{ id: 81, parentId: 80 }, { id: 76, parentId: 80 }],
},
{ id: 87, parentId: 86 },
],
},
],
};
原理
为什么可以这么做呢?这是因为,data
数组里的每个元素都是内存里的一个对象引用, forEach
循环里的el
变量其实是指向内存里的一个对象,parentEl
也引用了一个对象。
如果内存中的一个对象引用了一个 children 数组,这些子元素同样可以引用自己的子元素数组,这些关联关系都是通过引用完成的。
总结
对象引用是 JavaScript 中最基本的概念之一,需要更多的学习和理解。真正理解这个概念后,既可以避免棘手的 bug,又可以为看似复杂的问题提供相对简单的解决方案。
欢迎关注微信公众号:1024译站
JavaScript 构造树形结构的一种高效算法的更多相关文章
- 【WPF】树形结构TreeView的用法(MVVM)
TreeView控件的用法还是有蛮多坑点的,最好记录一下. 参考项目: https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-T ...
- Java编程:将具有父子关系的数据库表数据转换为树形结构,支持无限层级
在平时的开发工作中,经常遇到这样一个场景,在数据库中存储了具有父子关系的数据,需要将这些数据以树形结构的形式在界面上进行展示.本文的目的是提供了一个通用的编程模型,解决将具有父子关系的数据转换成树形结 ...
- javascript将平行的拥有上下级关系的数据转换成树形结构
转换函数 var Littlehow = {}; /** * littlehow 2019-05-15 * 平行数据树形转换器 * @type {{format: tree.format, sort: ...
- MongoDB五种树形结构表示法
MongoDB五种树形结构表示法 第一种:父链接结构 db.categories.insert( { _id: "MongoDB", parent: "Databases ...
- javascript通过递归改子节点数据-用于层级深度未知的树形结构
最近在做这么个需求:树形结构,层级深度未知,一旦某个节点的状态是置灰的话,其所有子节点都要置灰. 方案一(数据库有值):如果数据库里置灰节点的所有子节点,值也都是"置灰",那后台取 ...
- JavaScript中常见的十五种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”. 在JavaScript中并没有类这种概念,JS中的函数属于一等对象,在JS中定义一个对象非常简单(var obj = {}), ...
- 树形结构的数据库表Schema设计-基于左右值编码
树形结构的数据库表Schema设计 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门.栏目结构.商品分类等等,通常而言,这些树状结构需要借助于数据库完 成持久化.然而目前的 ...
- 浅谈树形结构的特性和应用(上):多叉树,红黑树,堆,Trie树,B树,B+树...
上篇文章我们主要介绍了线性数据结构,本篇233酱带大家康康 无所不在的非线性数据结构之一:树形结构的特点和应用. 树形结构,是指:数据元素之间的关系像一颗树的数据结构.我们看图说话: 它具有以下特点: ...
- C# EasyUI树形结构权限管理模块
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 本节和大家探讨下C#使用EasyUI树形结构/Tree构 ...
随机推荐
- qt 自定义窗口显示鼠标划过的轨迹
鼠标事件分为四种: 1.按下 2.抬起 3.移动 4.双击 鼠标事件继承与QWidget void mouseDoubleClickEvent(QMouseEvent *event) void mou ...
- Python基础:05集合类型
Python中,集合对象是一组无序排列的可哈希的值.所以集合成员可以做字典中的键.集合中的元素都是唯一的. 集合(sets)有两种不同的类型,可变集合(set) 和 不可变集合(frozenset). ...
- Flask学习之六 个人资料和头像
英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vi-profile-page-and-avatars ...
- MySQL按天,按周,按月,按时间段统计【转载】
自己做过MySQL按天,按周,按月,按时间段统计,但是不怎么满意,后来找到这位大神的博客,转载一下,谢谢这位博主的分享 知识点:DATE_FORMAT 使用示例 select DATE_FORMAT( ...
- part10.2-字符设备驱动模型
- H3C 常用信息查看命令
- iptables禁止ssh端口
只允许在192.168.62.1上使用ssh远程登录,从其它计算机上禁止使用ssh #iptables -A INPUT -s 192.168.62.1 -p tcp --dport 22 -j AC ...
- hdu 1358 Period (KMP求循环次数)
Problem - 1358 KMP求循环节次数.题意是,给出一个长度为n的字符串,要求求出循环节数大于1的所有前缀.可以直接用KMP的方法判断是否有完整的k个循环节,同时计算出当前前缀的循环节的个数 ...
- poj 3278(hdu 2717) Catch That Cow(bfs)
Catch That Cow Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- H3C 传递信息(续)