js处理扁平数组和树结构相互转换
一、将扁平的数据转为树形结构
在 js中,可以使用递归算法将扁平的数据转换为树形结构。
扁平数据通常是一个带有 parentId 属性的数组,而树形结构通常是一个带有 children 属性的对象。
1、方法一
下面是一个简单的例子,演示如何将扁平数组转换为树形对象:
let data = [{ id: 1, pid: 0, name: '中国' },{ id: 2, pid: 0, name: '巴铁' },{ id: 3, pid: 1, name: '小日本儿' },{ id: 4, pid: 2, name: '杂种美国' },{ id: 5, pid: 2, name: '俄罗斯' },{ id: 6, pid: 13, name: '乌克兰' },{ id: 7, pid: 3, name: '英国' },{ id: 8, pid: 7, name: '意大利' },{ id: 9, pid: 7, name: '西班牙' }
];function convertToTree(flatData) {let treeData = [];let map = new Map(); let outputObj, pid;for (let i = 0; i < flatData.length; i++) {pid = flatData[i].pid;if (map.has(pid)) {if (!map.get(pid).childrens)map.get(pid).childrens = [];let obj = new Object(flatData[i]);map.get(pid).childrens.push(obj);map.set(flatData[i].id, obj);} else if (!map.has(pid) && pid === 0) {// '&& pid === 0' 判断条件是为了只将 pid 为 0 作为根节点,其它值不作为根节点。// 如需将其它值也作为根节点,可将 'else if (!map.has(pid) && pid === 0)' 改为 'else'outputObj = new Object(flatData[i]);treeData.push(outputObj);map.set(flatData[i].id, outputObj);}}return treeData;
}let TreeData = convertToTree(data);
console.log(TreeData);
2、方法二
使用递归算法将扁平数组转换为树形对象:
const flatData = [{ id: 1, name: 'Node 1', parentId: null },{ id: 2, name: 'Node 2', parentId: 1 },{ id: 3, name: 'Node 3', parentId: 2 },{ id: 4, name: 'Node 4', parentId: 3 },{ id: 5, name: 'Node 5', parentId: 3 }
];function convertToTree(flatData, parentId = null) {const children = flatData.filter(node => node.parentId === parentId);if (!children.length) {return null;}return children.map(node => ({...node,children: convertToTree(flatData, node.id)}));
}const treeData = convertToTree(flatData);
console.log(treeData);
该算法的流程为:
- 使用 filter() 函数过滤出所有的子节点。
- 使用 map() 函数构造每个子节点的新结构,并使用递归来处理子节点的 children 属性。
3、方法三
还有一些第三方库可以帮助你转换扁平数据为树形结构,例如 lodash 中的 _.groupBy() 和 _.mapValues() 方法可以帮助你将扁平数据转换为树形数据。
const flatData = [{ id: 1, name: 'Node 1', parentId: null },{ id: 2, name: 'Node 2', parentId: null },{ id: 3, name: 'Node 3', parentId: 1 },{ id: 4, name: 'Node 4', parentId: 2 },{ id: 5, name: 'Node 5', parentId: 2 }
];const tree = _(flatData).groupBy('parentId').mapValues((children, parentId) => ({id: parentId || 'root',children: children.map(({ id, name, parentId }) => ({ id, name, parentId }))})).values().value();console.log(tree)
在这种情况下,假设parentId为null的数据项是根节点,那么所有其它的数据项的 parentId 分别对应它的父节点,我们可以使用 groupBy() 来将所有节点根据它们的 parentId 分组,然后我们可以使用 mapValues() 来构造每个组的新结构。
4、方法四
const flatData = [{ id: 1, name: 'Node 1', value: 1, parentId: null },{ id: 2, name: 'Node 2', value: 2, parentId: 1 },{ id: 3, name: 'Node 3', value: 3, parentId: 2 },{ id: 4, name: 'Node 4', value: 4, parentId: 3 },{ id: 5, name: 'Node 5', value: 5, parentId: 3 }
];/*** 将扁平数组转换为树形对象,* 适用于来自同一个表的数据,即idName的数据不重复* * @param {*} flatData 同级数组数据* @param {*} idName 唯一id* @param {*} pidName 父级id* @param {*} nameName 自定义(Cascader 级联选择器)键名称* @param {*} valueName 自定义(Cascader 级联选择器)值名称* @returns 树形结构数组数据,适用于Cascader 级联选择器组件*/
function convertToTree(flatData, idName, pidName, nameName, valueName) {let treeData = [];if (!Array.isArray(flatData)) {return treeData;}flatData.forEach((item) => {delete item.children;});let map = {};flatData.forEach((item) => {// 深拷贝,该方式将使该转换方法失效// map[item[idName]] = JSON.parse(JSON.stringify(item));// 浅拷贝,将对 item 的引用传递给 mapmap[item[idName]] = item;});flatData.forEach((item) => {// 深拷贝,该方式将使该转换方法失效// let parent = JSON.parse(JSON.stringify(map[item[pidName]]));// 浅拷贝,将对 map 的引用传递给 parentlet parent = map[item[pidName]];if (parent) {// 利用了浅拷贝的引用传递,最终 flatData 中的 item 将会改变(parent.children || (parent.children = [])).push(nameName && valueName ? Object.assign(item, { label: item[nameName], id: item[valueName] }) : item);} else {treeData.push(nameName && valueName ? Object.assign(item, { label: item[nameName], id: item[valueName] }) : item);}});return treeData;
}const treeData = convertToTree(flatData, 'id', 'parentId', 'name', 'value');
console.log(treeData);
二、树形转扁平
1、方法一
在 js中,可以使用递归算法将树形结构数据转换为扁平数组。
下面是一个简单的例子,它演示了如何将树形数据转换为扁平数组:
const treeData = [{id: 1,name: 'Node 1',children: [{ id: 2, name: 'Node 2', children: [{ id: 3, name: 'Node 3' }, { id: 4, name: 'Node 4' }] },{ id: 5, name: 'Node 5' }]
}];function convertToFlat(data, parentId = null) {return data.reduce((acc, curr) => {acc.push({ ...curr, parentId });if (curr.children) {acc = acc.concat(convertToFlat(curr.children, curr.id));}return acc;}, []);
}const flatData = convertToFlat(treeData);
console.log(flatData);
该算法的流程为:
- 使用 reduce() 函数遍历每个节点,并将父节点的 id 作为参数传递给递归函数。
- 使用 push() 函数将当前节点添加到结果数组中。
- 使用 concat() 函数将递归调用的结果与结果数组连接在一起。
- 如果当前节点有 children 属性,则递归调用 convertToFlat() 函数,并将当前节点的 id 作为父节点传递给函数。
注意:该方法返回的扁平结构数据未将 children属性删除,因此存在冗余的数据。
这是一种将树形结构数据转换为扁平数组的方法,如果有其他特定的需求,还可以使用其他方法来转换数据,例如使用广度优先遍历算法,使用队列存储节点。
2、方法二
const treeData = [{id: 1,name: 'Node 1',children: [{id: 2,name: 'Node 2',children: [{ id: 3, name: 'Node 3' },{ id: 4, name: 'Node 4' },]},{ id: 5, name: 'Node 5' },]},{id: 6,name: 'Node 6',children: [{ id: 7, name: 'Node 7' }]},
];function convertToFlat(treeData, parentId = null) {let flatData = [];for (let node of treeData) {flatData.push({ id: node.id, name: node.name, parentId });if (node.children) {flatData = flatData.concat(convertToFlat(node.children, node.id));}}return flatData;
}const flatData = convertToFlat(treeData);
console.log(flatData);
该算法的流程为:
- 创建一个空的扁平数组。
- 递归遍历树形数组中的每个节点,将当前节点添加到扁平数组中。
- 对于当前节点的子节点,继续使用递归,并将子节点添加到扁平数组中。
- 返回扁平数组
注意:该方法需要手动构造push 到扁平数组的对象,通用性较差。
3、方法三
在 js中,可以使用递归算法将树形结构数据转换为扁平数组。
下面是一个简单的例子,它演示了如何将树形数据转换为扁平数组:
let data = [{id: 1, pid: 0, name: '棒子', childrens: [{id: 2, pid: 1, name: '中国', childrens: [{ id: 4, pid: 2, name: '小日本儿' },{ id: 5, pid: 2, name: '杂种美国' }]},{id: 3, pid: 1, name: '俄罗斯', childrens: [{ id: 6, pid: 3, name: '巴铁' },{ id: 7, pid: 3, name: '阿三' }]}]
}];function convertToFlat(treeData) {let flatData = [];for (let i = 0; i < treeData.length; i++) {if (treeData[i].childrens) {flatData.push(...convertToFlat(treeData[i].childrens));delete treeData[i].childrens;}flatData.push({ ...treeData[i] });}return flatData;
}let flatData = convertToFlat(data);
console.log(flatData);
该方法通用性较强,对树形结构数组数据内部具体的属性名称要求较小(除 childrens)。
相关文章:
js处理扁平数组和树结构相互转换
一、将扁平的数据转为树形结构 在 js中,可以使用递归算法将扁平的数据转换为树形结构。 扁平数据通常是一个带有 parentId 属性的数组,而树形结构通常是一个带有 children 属性的对象。 1、方法一 下面是一个简单的例子,演示如何将扁平数…...
Spark弹性分布式数据集
1. Spark RDD是什么 RDD(Resilient Distributed Dataset,弹性分布式数据集)是一个不可变的分布式对象集合,是Spark中最基本的数据抽象。在代码中RDD是一个抽象类,代表一个弹性的、不可变、可分区、里面的元素可并行计…...
ffmpeg学习记录
1、对图片进行裁剪 ffmpeg -i input.jpg -vf cropiw/3:ih:20:0 caijian.jpg PS: crop100:100:12:34 相同效果: cropw100:h100:x12:y34 2、视频增加文字水印 使用drawtext滤镜进行增加水印 参数 类型 说明 text 字符串 文字 textfile 字符串 文字文件 …...
ChatGPT:为教育创新提供五大机遇
随着智能技术的不断发展,ChatGPT在教育场景中的创新价值可能比我们能够意识到的还要多。比如它可以自动处理作业、在线答疑,可以辅助语言学习、实时沟通,甚至还可以用于评估诊断、科学研究。国内外关于利用ChatGPT实现教育创新的场景描绘已经…...
Educational Codeforces Round 151 (Rated for Div. 2)
Edu 151 A. Forbidden Integer 题意: 你有[1, k]内除了 x x x的整数,每个数可以拿多次,问 ∑ n \sum n ∑n是否可行并构造 思路: 有1必能构造,否则假如没有1,假如有2, 3必定能构造出大于等于2的所有数&…...
【AI机器学习入门与实战】机器学习算法都有哪些分类?
👍【AI机器学习入门与实战】目录 🍭基础篇 🔥 第一篇:【AI机器学习入门与实战】AI 人工智能介绍 🔥 第二篇:【AI机器学习入门与实战】机器学习核心概念理解 🔥 第三篇:【AI机器学习入…...
React之hooks
Hooks函数 1.useState():状态钩子。纯函数组件没有状态,用于为函数组件引入state状态, 并进行状态数据的读写操作。 const [state, setState] useState(initialValue); // state:初始的状态属性,指向状态当前值,类似…...
1.监控分布式--zabbix
文章目录 监控分布式-zabbix、prometheus概念工作原理功能组件部署zabbix安装Nginx和PHP环境部署数据库编码安装zabbix编译安装zabbix server客户端安装zabbix agent服务 监控分布式-zabbix、prometheus 利用一个优秀的监控软件,我们可以: 通过一个友好的界面进行…...
java stream 多个集合去重取交集
文章目录 背景案例代码 背景 原因是需要从表里查多个集合list,然后取多个集合得交集,并且元素是对象,所以使用了下面的方式,当然方式有很多种,仅供参考。 案例 下面提供了一段多个集合join取交集的例子,…...
给LLM装上知识:从LangChain+LLM的本地知识库问答到LLM与知识图谱的结合
第一部分 什么是LangChain:连接本地知识库与LLM的桥梁 作为一个 LLM 应用框架,LangChain 支持调用多种不同模型,提供相对统一、便捷的操作接口,让模型即插即用,这是其GitHub地址,其架构如下图所示 (点此查…...
视频与AI,与进程交互(二) pytorch 极简训练自己的数据集并识别
目标学习任务 检测出已经分割出的图像的分类 2 使用pytorch pytorch 非常简单就可以做到训练和加载 2.1 准备数据 如上图所示,用来训练的文件放在了train中,验证的文件放在val中,train.txt 和 val.txt 分别放文件名称和分类类别ÿ…...
LLM - 第2版 ChatGLM2-6B (General Language Model) 的工程配置
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/131445696 ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,在保留了初代模型对话流畅、部署门槛较低等众多优…...
从0开始,手写MySQL事务
说在前面:从0开始,手写MySQL的学习价值 尼恩曾经指导过的一个7年经验小伙,凭借精通Mysql, 搞定月薪40K。 从0开始,手写一个MySQL的学习价值在于: 可以深入地理解MySQL的内部机制和原理,Mysql可谓是面试的…...
React中useState的setState方法请求了好多次
1、问题描述 最近在写react的时候碰到了一个很奇怪的问题。 可以看到那个getXXX()的方法一直不断的被调用,网页一直请求,根本停不下来了。 2、产生原因 要弄明白这个原因,首先要先了解一下react生命周期。 react是组件式的编程,一…...
【MYSQL基础】基础命令介绍
基础命令 MYSQL注释方式 -- 单行注释/* 多行注释 哈哈哈哈哈 哈哈哈哈 */连接数据库 mysql -u root -p12345678退出数据库连接 使用exit;命令可以退出连接 查询MYSQL版本 mysql> select version(); ----------- | version() | ----------- | 8.0.27 | ----------- 1…...
多元回归预测 | Matlab基于灰狼算法优化深度置信网络(GWO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型
文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于灰狼算法优化深度置信网络(GWO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型,matlab代码回归预测,多变量输入模型,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质…...
校园wifi网页认证登录入口
很多校园wifi网页认证登录入口是1.1.1.1 连上校园网在浏览器写上http://1.1.1.1就进入了校园网 使 用 说 明 一、帐户余额 < 0.00元时,帐号被禁用,需追加网费。 二、在计算中心机房上机的用户,登录时请选择新建帐号时给您指定的NT域&…...
[SpringBoot]Spring Security框架
目录 关于Spring Security框架 Spring Security框架的依赖项 Spring Security框架的典型特征 关于Spring Security的配置 关于默认的登录页 关于请求的授权访问(访问控制) 使用自定义的账号登录 使用数据库中的账号登录 关于密码编码器 使用BCry…...
Unity 之 抖音小游戏本地数据最新存储方法分享
Unity 之 抖音小游戏本地数据最新存储方法分享 一、抖音小游戏文件存储系统背景二、文件存储系统的使用方法2.1 初始化2.1 创建目录2.3 存储数据2.4 删除目录/文件2.5 其他相关操作 三,小结 抖音小游戏是一种基于抖音平台开发的小型游戏,与传统的 APP 不…...
逍遥自在学C语言 | 函数初级到高级解析
前言 函数是C语言中的基本构建块之一,它允许我们将代码组织成可重用、模块化的单元。 本文将逐步介绍C语言函数的基础概念、参数传递、返回值、递归以及内联函数和匿名函数。 一、人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 —— 自在…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
