当前位置: 首页 > news >正文

JS前端树形Tree数据结构使用

前端开发中会经常用到树形结构数据,如多级菜单、商品的多级分类等。数据库的设计和存储都是扁平结构,就会用到各种Tree树结构的转换操作,本文就尝试全面总结一下。

如下示例数据,关键字段id为唯一标识,pid父级id,用来标识父级节点,实现任意多级树形结构。"pid": 0“0”标识为根节点,orderNum属性用于控制排序。

const data = [{ "id": 1, "name": "用户中心", "orderNum": 1, "pid": 0 },{ "id": 2, "name": "订单中心", "orderNum": 2, "pid": 0 },{ "id": 3, "name": "系统管理", "orderNum": 3, "pid": 0 },{ "id": 12, "name": "所有订单", "orderNum": 1, "pid": 2 },{ "id": 14, "name": "待发货", "orderNum": 1.2, "pid": 2 },{ "id": 15, "name": "订单导出", "orderNum": 2, "pid": 2 },{ "id": 18, "name": "菜单设置", "orderNum": 1, "pid": 3 },{ "id": 19, "name": "权限管理", "orderNum": 2, "pid": 3 },{ "id": 21, "name": "系统权限", "orderNum": 1, "pid": 19 },{ "id": 22, "name": "角色设置", "orderNum": 2, "pid": 19 },
];

在前端使用的时候,如树形菜单、树形列表、树形表格、下拉树形选择器等,需要把数据转换为树形结构数据,转换后的数据结效果图:

 

预期的树形数据结构:多了children数组存放子节点数据。

const treeData = [{ "id": 1, "name": "用户中心", "pid": 0 },{"id": 2, "name": "订单中心", "pid": 0,"children": [{ "id": 12, "name": "所有订单", "pid": 2 },{ "id": 14, "name": "待发货", "pid": 2 },{ "id": 15, "name": "订单导出","pid": 2 }]},{"id": 3, "name": "系统管理", "pid": 0,"children": [{ "id": 18, "name": "菜单设置", "pid": 3 },{"id": 19, "name": "权限管理", "pid": 3,"children": [{ "id": 21, "name": "系统权限",  "pid": 19 },{ "id": 22, "name": "角色设置",  "pid": 19 }]}]}
]

列表转树-list2Tree

方法一 递归遍历

从根节点递归,查找每个节点的子节点,直到叶子节点(没有子节点)

//递归函数,pid默认0为根节点
function listToTree(items, pid = 0) {//查找pid子节点let pitems = items.filter(s => s.pid === pid)if (!pitems || pitems.length <= 0)return null//递归pitems.forEach(item => {const res = buildTree(items, item.id)if (res && res.length > 0)item.children = res})return pitems
}
方法二 object的Key遍历

简单理解就是一次性循环遍历查找所有节点的父节点,两个循环就搞定了。

  • 第一次循环,把所有数据放入一个Object对象map中,id作为属性key,这样就可以快速查找指定节点了。
  • 第二个循环获取根节点、设置父节点。

分开两个循环的原因是无法完全保障父节点数据一定在前面,若循环先遇到子节点,map中还没有父节点的,否则一个循环也是可以的。

/*** 集合数据转换为树形结构。option.parent支持函数,示例:(n) => n.meta.parentName* @param {Array} list 集合数据* @param {Object} option 对象键配置,默认值{ key: 'id', parent: 'pid', children: 'children' }* @returns 树形结构数据tree*/
export function listToTree(list, option = { key: 'id', parent: 'pid', children: 'children' }) {let tree = []// 获取父编码统一为函数let pvalue = typeof (option.parent) === 'function' ? option.parent : (n) => n[option.parent]// map存放所有对象let map = {}list.forEach(item => {map[item[option.key]] = item})//遍历设置根节点、父级节点list.forEach(item => {if (!pvalue(item))tree.push(item)else {map[pvalue(item)][option.children] ??= []map[pvalue(item)][option.children].push(item)}})return tree
}

树转列表-tree2List

从上而下依次遍历,把所有节点都放入一个数组中即可

/*** 树形转平铺list(广度优先,先横向再纵向)* @param {*} tree 一颗大树* @param {*} option 对象键配置,默认值{ children: 'children' }* @returns 平铺的列表*/
export function tree2List(tree, option = { children: 'children' }) {const list = []const queue = [...tree]while (queue.length) {const item = queue.shift()if (item[option.children]?.length > 0)queue.push(...item[option.children])list.push(item)}return list
}

搜索过滤树-filterTree

基本思路:

  • 为避免污染原有Tree数据,这里的对象都使用了简单的浅拷贝const newNode = { ...node }
  • 递归为主的思路,子节点有命中,则会包含父节点,当然父节点的children会被重置。
/*** 递归搜索树,返回新的树形结构数据,只要子节点命中保留其所有上级节点* @param {Array|Tree} tree 一颗大树* @param {Function} func 过滤函数,参数为节点对象* @param {Object} option 对象键配置,默认值{ children: 'children' }* @returns 过滤后的新 newTree*/
export function filterTree(tree, func, option = { children: 'children' }) {let resTree = []if (!tree || tree?.length <= 0) return nulltree.forEach(node => {if (func(node)) {// 当前节点命中const newNode = { ...node }if (node[option.children])newNode[option.children] = null //清空子节点,后面递归查询赋值const cnodes = filterTree(node[option.children], func, option)if (cnodes && cnodes.length > 0)newNode[option.children] = cnodesresTree.push(newNode)}else {// 如果子节点有命中,则包含当前节点const fnode = filterTree(node[option.children], func, option)if (fnode && fnode.length > 0) {const newNode = { ...node, [option.children]: null }newNode[option.children] = fnoderesTree.push(newNode)}}})return resTree
}

相关文章:

JS前端树形Tree数据结构使用

前端开发中会经常用到树形结构数据&#xff0c;如多级菜单、商品的多级分类等。数据库的设计和存储都是扁平结构&#xff0c;就会用到各种Tree树结构的转换操作&#xff0c;本文就尝试全面总结一下。 如下示例数据&#xff0c;关键字段id为唯一标识&#xff0c;pid为父级id&am…...

Automation Anywhere推出新的生成式AI自动化平台,加速提高企业生产力

在9 月 19 日的Imagine 2023 大会上&#xff0c;智能自动化领域的领导者 Automation Anywhere 宣布对其自动化平台进行扩展。推出了新的 Responsible AI Layer&#xff0c;并宣布了四项关键产品更新&#xff0c;包括全新的 Autopilot&#xff0c;它可以利用生成式 AI &#xff…...

电缆隧道在线监测系统:提升电力设施安全与效率的关键

随着城市化进程的加快&#xff0c;电力电缆隧道在保障城市电力供应方面的地位日益重要。然而&#xff0c;电缆隧道环境复杂&#xff0c;容易受到多种因素影响&#xff0c;如温度、湿度、烟雾、水位等&#xff0c;严重威胁电力设施的安全与稳定运行。在此背景下&#xff0c;电缆…...

Java BigDecimal 详解

目录 一、BigDecimal 1、简介 2、构造器描述 3、方法描述 4、使用 一、BigDecimal float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff0c;它…...

简述信息论与采样定理

信息论 香农信息论发表于1948/1949年&#xff0c;它由三部分组成&#xff1a;信号采样、信源编码、信道编码&#xff1b; 信号采样&#xff1a;采样理论研究在何种条件下对连续信号进行采样&#xff0c;从而得到的离散型号可以可逆地恢复出采样前的连续信号。采样得到的离散实…...

网络安全之网站常见的攻击方式

这是作者自学的哈&#xff0c;不算课程内容。 网页中出现大量黑链 网站看着很正常&#xff0c;但是会隐藏一些链接。网页的链接几乎都是标签&#xff0c;这种黑链就是通过链接标签<a></a>或者script在里面链入恶意脚本&#xff0c;等待浏览者的访问&#xff0c;通…...

iOS Swift 拍照识别数字(Recognizing Text in Images)

可以用腾讯云 OCR的iOS demo - 腾讯云 苹果官方的解决方案&#xff08;识别度太低&#xff09; Recognizing Text in Images - apple developer Extracting phone numbers from text in images(Sample Code) - apple developer import UIKit import Visionclass ViewContro…...

数学建模:智能优化算法及其python实现

数学建模:智能优化算法及其python实现 智能优化算法简介差分进化算法(Differential Evolution,DE)遗传算法(Genetic Algorithm,GA)粒子群优化算法(Particle Swarm Optimization,PSO)模拟退火算法(Simulated Annealing,SA)蚁群算法(Ant Colony Optimization,ACO)…...

monkeyrunner环境搭建和初步用法

一、打开模拟器 运行monkeyrunner之前必须先运行相应的模拟器&#xff0c;不然monkeyrunner无法连接设备。 用Elipse打开Android模拟器或在CMD中用Android命令打开模拟器。这里重点讲一下在CMD中用Android命令打开模拟器 命令&#xff1a;emulator -avd test &#xff08;注…...

2024华为校招面试真题汇总及其解答(一)

1. 我问你点java基础的问题吧,你平时都用什么集合啊,都什么情况下使用 在 Java 中,常用的集合有以下几种: List:有序集合,可以重复,常用实现类有 ArrayList、LinkedList、Vector。Set:无序集合,不能重复,常用实现类有 HashSet、TreeSet。Map:键值对集合,键不能重复…...

css调整字体间距 以及让倾斜字体

调整字体间距 .element {letter-spacing: 2px; /* 调整为适当的值 */ }倾斜字体1 .element {font-style: italic; }请注意&#xff0c;不是所有的字体都有斜体样式可用。如果字体本身没有斜体版本&#xff0c;则可能无法实现完全的斜体效果。 倾斜字体2 <span class"…...

工具篇 | Gradle入门与使用指南 - 附Github仓库地址

介绍 1.1 什么是Gradle&#xff1f; Gradle是一个开源构建自动化工具&#xff0c;专为大型项目设计。它基于DSL&#xff08;领域特定语言&#xff09;编写&#xff0c;该语言是用Groovy编写的&#xff0c;使得构建脚本更加简洁和强大。Gradle不仅可以构建Java应用程序&#x…...

使用 Python 函数callable和isinstance的意义

一、说明 在这篇博客中&#xff0c;我们将探讨两个python函数&#xff1a;1 callable 中的函数及其有趣的应用程序。该callable函数用于检查对象是否可调用&#xff0c;这意味着它可以作为函数调用。2 isinstance这个内置函数允许我们比较两种不同的数据类型并确定它们是否相…...

Netty场景及其原理

Netty场景及其原理 Netty简化Java NIO的类库的使用&#xff0c;包括Selector、 ServerSocketChannel、 SocketChannel、ByteBuffer&#xff0c;解决了断线重连、 网络闪断、心跳处理、半包读写、 网络拥塞和异常流的处理等。Netty拥有高性能、 吞吐量更高&#xff0c;延迟更低…...

Java接口和接口继承

Java接口和接口继承 接口 在抽象类中&#xff0c;抽象方法本质上是定义接口规范&#xff0c;即规定高层类的接口&#xff0c;从而保证所有子类都有相同的接口实现&#xff0c;这样&#xff0c;多态就能发挥出威力。 如果一个抽象类没有字段&#xff0c;所有方法全部都是抽象方…...

2023 年解锁网络安全即服务

在当今快速发展的数字世界中&#xff0c;强大的网络安全机制的重要性怎么强调都不为过。对于越来越多地发现自己成为网络威胁焦点的小型企业来说尤其如此。 那么&#xff0c;“网络安全即服务”到底是什么&#xff1f;为什么它对小型企业至关重要&#xff1f; 网络安全即服务…...

python基于轻量级卷积神经网络模型GhostNet开发构建养殖场景下生猪行为识别系统

养殖业的数字化和智能化是一个综合应用了互联网、物联网、人工智能、大数据、云计算、区块链等数字技术的过程&#xff0c;旨在提高养殖效率、提升产品质量以及促进产业升级。在这个过程中&#xff0c;养殖生猪的数字化智能化可以识别并管理猪的行为。通过数字化智能化系统&…...

Selenium自动化测试 —— 通过cookie绕过验证码的操作!

验证码的处理 对于web应用&#xff0c;很多地方比如登录、发帖都需要输入验证码&#xff0c;类型也多种多样&#xff1b;登录/核心操作过程中&#xff0c;系统会产生随机的验证码图片&#xff0c;进行验证才能进行后续操作 解决验证码的方法如下&#xff1a; 1、开发做个万能…...

链表(单链表、双链表)

前言&#xff1a;链表是算法中比较难理解的部分&#xff0c;本博客记录单链表、双链表学习&#xff0c;理解节点和指针的使用&#xff0c;主要内容包括&#xff1a;使用python创建链表、实现链表常见的操作。 目录 单链表 双链表 单链表 引入链表的背景&#xff1a; 先来看…...

面试题08.05.递归算法

递归乘法。 写一个递归函数&#xff0c;不使用 * 运算符&#xff0c; 实现两个正整数的相乘。可以使用加号、减号、位移&#xff0c;但要吝啬一些。 示例1: 输入&#xff1a;A 1, B 10输出&#xff1a;10示例2: 输入&#xff1a;A 3, B 4输出&#xff1a;12提示: 保证乘法…...

QIP 2023:亚马逊量子计算三篇论文突破

量子技术 某机构在QIP 2023发表的量子计算论文 针对“超级Grover”优化、拓扑数据分析的量子算法以及物理系统模拟的研究&#xff0c;展示了某机构在量子计算领域的广泛兴趣。 作者&#xff1a; Fernando Brando 日期&#xff1a; 2023年2月2日 阅读时间&#xff1a; 7分钟 在今…...

C1083编译错误:解决‘xxx.h‘文件缺失问题的实战指南

1. 遇到C1083编译错误时的心态调整 第一次看到"C1083 无法打开包括文件: xxx.h: No such file or directory"这个错误提示时&#xff0c;我正熬夜赶一个Qt界面开发项目。当时整个人都懵了&#xff0c;明明昨天还能正常编译的代码&#xff0c;怎么突然就报错了&#x…...

统信UOS安装踩坑实录:Win7老用户用balenaEtcher制作启动盘的那些事儿

统信UOS安装实战&#xff1a;Win7环境下避坑指南与工具选择 作为一个长期使用Windows 7的老用户&#xff0c;最近尝试安装统信UOS操作系统时&#xff0c;遇到了不少意料之外的挑战。特别是在制作启动盘这个看似简单的环节&#xff0c;各种问题接踵而至——U盘无法识别、烧录后启…...

Matlab进阶技巧:如何用hatchfill2和legendflex打造专业级纹理柱状图

Matlab数据可视化进阶&#xff1a;用hatchfill2与legendflex打造学术级纹理柱状图 在科研论文或商业报告中&#xff0c;单调的纯色柱状图往往难以清晰传达多维数据的层次关系。当需要区分5种以上的数据类别时&#xff0c;即使用尽所有高对比度颜色&#xff0c;依然会面临辨识度…...

为什么事故复盘总是写到很晚?

这两天和几个做运维/后端的朋友聊了下事故复盘&#xff0c;发现一个很真实的情况&#xff1a; &#x1f449; 大家都知道复盘很重要 &#x1f449; 但几乎没人愿意写 我问了一个问题&#xff1a; “为什么一份复盘总是要写那么久&#xff1f;” 总结下来基本都是这几个原因&…...

BilibiliDown终极指南:简单快速下载B站视频的完整教程

BilibiliDown终极指南&#xff1a;简单快速下载B站视频的完整教程 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors/b…...

革命性AI肖像动画工具LivePortrait:一键让静态照片“动“起来

革命性AI肖像动画工具LivePortrait&#xff1a;一键让静态照片"动"起来 【免费下载链接】LivePortrait Bring portraits to life! 项目地址: https://gitcode.com/GitHub_Trending/li/LivePortrait 你是否曾经想过让老照片中的亲人重新展露笑容&#xff1f;或…...

C#多线程编程实战:Interlocked类如何帮你避免数据竞争(附性能对比)

C#多线程编程实战&#xff1a;Interlocked类如何帮你避免数据竞争&#xff08;附性能对比&#xff09; 当你在开发一个需要处理高并发的C#应用时&#xff0c;是否遇到过计数器结果不准确、标志位莫名其妙被重置的诡异情况&#xff1f;这些看似简单的多线程问题&#xff0c;往往…...

RTX 4060笔记本也能玩转AI绘画?Nunchaku FLUX.1-dev量化版亲测体验报告

RTX 4060笔记本也能玩转AI绘画&#xff1f;Nunchaku FLUX.1-dev量化版亲测体验报告 1. 开箱即用的AI绘画体验 作为一名长期使用中端显卡的AI爱好者&#xff0c;当我第一次听说Nunchaku FLUX.1-dev量化版可以在RTX 4060笔记本上运行时&#xff0c;内心充满了怀疑。毕竟&#x…...

利用UptimeFlare与Cloudflare Workers自动化保活Huggingface Space

1. 为什么需要保活Huggingface Space Huggingface Space是个好东西&#xff0c;能让我们免费部署各种AI应用。但有个头疼的问题&#xff1a;如果48小时内没人访问&#xff0c;Space就会自动休眠。下次有人访问时&#xff0c;又要重新启动&#xff0c;等得花儿都谢了。我自己做…...