30行代码撸一个性能勉强靠谱的构建树逻辑
2018-01-31 17:15
204 查看
大半年前写了一个构建树逻辑,呱呱呱,感觉不够优雅,感觉自己没有对细节做考虑,代码也不够精简,于是自己又动手写了写,整体思想其实差不多,但是针对细节做了一些优化,优化的过程也让我更一步理解了引用类型的数据,也让我发现forEach方法的没注意到的点:就是在用forEach对数组做循环的时候,你删除了数组的一个元素,forEach并没有帮你把循环过程的索引往前移一位,也就是做一个i–的过程,导致整个循环过程会丢失一些元素,所以我使用for循环手动i–。因为这里我是这么想的:一个元素只可能有一个父元素,所以我在对整个的数组做循环的时候,删除已经找到父节点的元素会让整个递归过程更快,整体的思想:就是找到第一级节点,然后递归给第一级节点找子节点,然后递归再给子节点找子子节点,依次递归下去
实际测试
其实很多原生函数返回的对象都是和原先的对象共享内存地址的,比如find, map等等,你可以了解一下堆栈的概念会帮助你更清楚的理解一些js原生函数的返回值,比如字符串的replace函数肯定是返回了一个新字符而不是在原有字符串上做修改,知识点到为止,自己带着问题去找为什么才是正确学习的方法
function hasParentNode(data, item) { // 是否存在父节点 const index = data.findIndex(c => c.id === item.parentId) if (~index) return true } function getChildrenNodes(parentNodes, allNodes) { // 获取每一个节点的子节点 if (parentNodes.length === 0) return const node = parentNodes.shift() /* allNodes.forEach((item, index) => { // 这里本来是原本的想法,函数式forEach比for循环更优雅 if (node.id === item.parentId) { if (node.children) node.children.push(item) else node.children = [item] parentNodes.push(item) allNodes.splice(index, 1) } }) */ for (let i = 0; i < allNodes.length; i += 1) { const current = allNodes[i] if (node.id === current.parentId) { if (node.children) node.children.push(current) else node.children = [current] parentNodes.push(current) allNodes.splice(i, 1) // 删除已经找到父节点的元素 i -= 1 // 删除元素后把索引往前挪一位 } } getChildrenNodes(parentNodes, allNodes) } function buildTree(data) { const parentNodes = []; // 有子节点的父级节点数组 data.forEach((item) => { if (!hasParentNode(data, item)) parentNodes.push(item) }) if (parentNodes.length <= 0) return const dataSource = [...parentNodes] // 最终数组,引用类型的缘故,你操作parentNodes的每一个元素的过程就是操作dataSource的每一个元素的过程,我经常利用这个特性,如果你不想这样,你可能需要深拷贝复制一个新数组,有时候妙用这一特性能给你带来很多的便利 getChildrenNodes(parentNodes, data) return dataSource }
实际测试
const source = [ { id: 1, parentId: undefined, groupName: { name: '经理1', count: 10 }, }, { id: 2, parentId: null, groupName: { name: '经理2', count: 1 }, }, { id: 3, parentId: 0, groupName: { name: '经理3', count: 2 }, }, { id: 4, parentId: 1, groupName: { name: '主管1', count: 4 }, }, { id: 5, parentId: 1, groupName: { name: '主管2', count: 6 }, }, { id: 6, parentId: 4, groupName: { name: '小组1', count: 4 }, }, { id: 7, parentId: 5, groupName: { name: '小组2', count: 4 }, }, { id: 8, parentId: 2, groupName: { name: '主管1', count: 0 }, }, { id: 9, parentId: 3, groupName: { name: '主管1', count: 0 }, }, ]; console.log(buildTree(source))
其实很多原生函数返回的对象都是和原先的对象共享内存地址的,比如find, map等等,你可以了解一下堆栈的概念会帮助你更清楚的理解一些js原生函数的返回值,比如字符串的replace函数肯定是返回了一个新字符而不是在原有字符串上做修改,知识点到为止,自己带着问题去找为什么才是正确学习的方法
let a = [{id: 1, name: 'lane'},{id: 2, name: 'lc'}] let element = a.find((item) => item.id === 2) element.name = '数据改变了,引用没有变' console.log(a)
相关文章推荐
- 用JavaScript实现一个代码简洁、逻辑不复杂的多级树
- 更换头像的逻辑代码,两个功能,一个是从图库选择,一个是打开相机自己拍照
- 用500行纯前端代码在浏览器中构建一个Tableau
- 多个TermQuery或一个MultiFieldQueryParser构建BooleanQuery多个域的检索学习实例代码
- Java代码构建一个线程池 Java,线程池 [程序开发Java] IT.com.cn IT世界网
- 用Java代码构建一个线程池
- 概述 RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范(Asynchronou
- 代码的世界中,一个逻辑套着另外一个逻辑,如何让每一种逻辑在代码中都有迹可循?
- 构建的可伸缩性和达到的性能:一个虚拟座谈会
- 100行Java代码构建一个线程池
- 50 行 Python 代码构建一个区块链
- 假装是一个成功的布局——3、java逻辑代码
- peercast 代码分析(2)——构建一个lib,并继承其中的基类
- Java代码构建一个线程池或连接池
- asp.net 的一个新bug. 当你的程序出问题时, 不仅仅要检查自己的代码和逻辑, 还要检查微软的代码, 累吗?
- Java代码构建一个线程池
- 多个TermQuery或一个MultiFieldQueryParser构建BooleanQuery多个域的检索学习实例代码
- 100行Java代码构建一个线程池
- 50 行 Python 代码构建一个区块链
- 块设备驱动实战基础篇一 (170行代码构建一个逻辑块设备驱动)