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

news2024/11/27 11:49:29

前端开发中会经常用到树形结构数据,如多级菜单、商品的多级分类等。数据库的设计和存储都是扁平结构,就会用到各种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 null
  tree.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] = cnodes
      resTree.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] = fnode
        resTree.push(newNode)
      }
    }
  })
  return resTree
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1042347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

稳压器【TPS6283810YFPR 3A】汽车类、TPS629203QDRLRQ1,TPS74550PQWDRVRQ1采用小型6 引脚 WSON 封装。

一、TPS6283810、采用 1.2mm x 0.8mm WCSP 封装的 2.4V-5.5V 输入、6 引脚 3A 微型降压转换器 TPS6283810YFPR是一款高频同步降压转换器&#xff0c;经优化具有小解决方案尺寸和高效率等特性。该器件的输入电压范围为2.4V 至 5.5V&#xff0c;支持常用电池技术。该转换器在中等…

如何永久关闭WPS任务窗口?

1、按住任务窗口上的浮动按钮&#xff0c;将其拖出来成悬浮窗口。 第二步&#xff0c;使用火绒弹窗拦截&#xff0c;选中弹出的窗口&#xff0c;进行拦截。注意&#xff1a;拦截次数为2次。即进行2次操作。 操作两次后&#xff0c;弹窗被拦截&#xff0c;此时Word文档改为双页显…

众佰诚:现在的抖音小店赚钱是真的吗

随着互联网的飞速发展&#xff0c;社交媒体平台如抖音已经成为了许多人赚钱的新途径。在抖音上&#xff0c;越来越多的小店涌现出来&#xff0c;各种各样的产品被推销给用户。但是&#xff0c;人们普遍关心的一个问题是&#xff1a;现在的抖音小店赚钱是真的吗? 首先&#xff…

新闻报道的未来:自动化新闻生成与爬虫技术

概述 自动化新闻生成是一种利用自然语言处理和机器学习技术&#xff0c;从结构化数据中提取信息并生成新闻文章的方法。它可以实现大规模、高效、多样的新闻内容生产。然而&#xff0c;要实现自动化新闻生成&#xff0c;首先需要获取可靠的数据源。这就需要使用爬虫技术&#…

【慕伏白教程】 Linux 深度学习服务器配置指北

文章目录 镜像烧录系统安装系统配置常用包安装 镜像烧录 下载 Ubuntu 镜像 Ubuntu 桌面版 下载烧录工具 balenaEtcher 准备至少 8G 的 空白U盘 开始烧录 系统安装 开机进入BIOS&#xff0c;修改U盘为第一启动 选择 Try or Install Ubuntu 往下拉&#xff0c;选择 中文&a…

14:00面试,14:06就出来了,这问的谁顶得住啊

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%,…

视频汇聚平台EasyCVR从一分屏切换到四分屏后加载记录显示黑屏该如何解决?

视频汇聚/视频云存储/集中存储/视频监控管理平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅、全网分发、云存储、智能分析等&#xff0c;视频智能分析平台EasyCVR融合性强、开放度…

order模块给User模块发送http请求

spring提供了一个工具叫做RestTemplate,这个工具就是spring提供给我们来发送http请求的 1.首先在配置类里面通过bean的形式将RestTemplate注册为spring的一个对象 细节:启动类也是一个配置类

【halcon特征点专题系列】01/4--Harris角点检测

一、说明 Harris角点检测是一种常用的计算机视觉算法,用于检测图像中的角点。它的原理是使用自适应窗口,在每个像素处计算其在该窗口内的灰度值变化量,即在不同方向上移动窗口后的灰度值变化量的平方和。如果该值超过一个阈值,则认为该像素是角点,因为角点处存在着较大的灰…

UG\NX二次开发 信息窗口的一些操作 NXOpen/ListingWindow

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介: UG\NX二次开发 信息窗口的一些操作 NXOpen/ListingWindow 效果: 代码: #include "me.hpp" #include <NXOpen/ListingWindow.hxx> #include <…

c/c++里 对 共用体 union 的内存分配

对union 的内存分配&#xff0c;是按照最大的那个成员分配的。 谢谢

【C++】手撕string(string的模拟实现)

手撕string目录&#xff1a; 一、 Member functions 1.1 constructor 1.2 Copy constructor&#xff08;代码重构&#xff1a;传统写法和现代写法&#xff09; 1.3 operator&#xff08;代码重构&#xff1a;现代写法超级牛逼&#xff09; 1.4 destructor 二、Other mem…

Nature子刊|Binning宏基因组+单菌验证

土壤砷污染是全球性环境问题&#xff0c;在水稻土壤中主要以二甲基砷&#xff08;DMAs&#xff09;形态存在&#xff0c;过量积累DMAs会诱发“直穗病”&#xff0c;影响水稻结实。目前尚无有效的防控措施。今天给大家带来的分享是南京农业大学赵方杰教授团队与中科院微生物所东…

惠普战99移动工作站: 第十三代英特尔酷睿处理器和惠普一站式AI应用开发方案的完美融合

近年来&#xff0c;随着办公移动化、自动化、智能化的需求日益增加&#xff0c;高性能工作站在各行各业的应用中愈加广泛。但传统的台式工作站由于体积大、重量重、缺乏灵活性&#xff0c;已经难以满足许多场景的需求。不过&#xff0c;得益于硬件技术和制造工艺的持续发展&…

CSP-J第二轮试题-2022年-1.2题

文章目录 参考&#xff1a;总结 [CSP-J 2022] 乘方题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示结题思路 [CSP-J 2022] 解密题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 现场真题注意事项 参考&#xff1a; 总…

MySQL 子查询(多表查询 三)

概念 SQL语句中嵌套select语句&#xff0c;称为嵌套查询&#xff0c;又称子查询&#xff0c;它可以在查询过程中使用其他查询的结果。 基本语法&#xff1a; select * from t1 where column1 ( select column1 from t2 ); 子查询外部的语句可以是insert/ update/ delete/ …

Docker的学习记录

Docker是一个被广泛使用的开源容器引擎&#xff0c;基于Go语言&#xff0c;遵从Apache2.0协议开源。 docker的三个概念&#xff1a;容器、镜像和仓库。 镜像&#xff08;Image&#xff09;&#xff1a;镜像是Docker中的一个模板。通过 Docker镜像 来创建 Docker容器&#xff…

四川玖璨公司抖音收费多少?

首先&#xff0c;从公司背景来看&#xff0c;四川玖璨电子商务有限公司是一家专注于电子商务领域的公司&#xff0c;经验丰富且有一支优秀的团队。作为一家专业的抖音培训公司&#xff0c;他们推出的抖音培训项目肯定是建立在自己经验与实践的基础上&#xff0c;并且对市场的变…

SpringBoot2.x整合Jedis客户端详细过程

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

dependencies

这个dependencies 软件显示dll的functions有的时候不全。 他本来可以看function。但是有时构造体的自动生成的构造函数是没有的。 这个dll在生成lib里面其实会有自动生成的构造函数。