Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
将扁平对象数组转换为多层级嵌套对象_创想鸟

将扁平对象数组转换为多层级嵌套对象

将扁平对象数组转换为多层级嵌套对象

本文详细介绍了如何利用JavaScript的`Array.prototype.reduce`方法,将一个包含父子关系信息的扁平对象数组高效地转换为一个多层级嵌套的对象结构。通过巧妙运用空值合并赋值运算符(`??=`),我们能以简洁的方式处理多层级嵌套,构建出符合预期树状结构的复杂对象。

在现代JavaScript开发中,数据结构的转换是一项常见任务。有时,我们会遇到一种扁平化的数据表示,其中每个对象都通过一个parent字段来指示其父级,但我们最终需要一个多层级嵌套的对象来更好地反映其层级关系。本教程将深入探讨如何高效地实现这种转换,尤其是在处理2、3层甚至更多层级嵌套时遇到的挑战。

理解问题:扁平数据与目标结构

我们通常会从一个数组开始,其中每个元素都是一个包含key、value和parent属性的对象。parent字段指定了当前对象应该归属的父级key。如果parent为空,则表示该对象是顶级元素。

输入示例:

const data = [    { "rank": 0, "key": "REPORTING PERIOD", "value": "2022", "parent": "" },    { "rank": 0, "key": "SIGNATURE DATE", "value": "20211005", "parent": "" },    { "rank": 0, "key": "HOUSE", "value": "", "parent": "" },    { "rank": 1, "key": "OWNER DATA", "value": "", "parent": "HOUSE" },    { "rank": 2, "key": "FIRST NAME", "value": "Joe", "parent": "OWNER DATA" },    { "rank": 2, "key": "LAST NAME", "value": "Smith", "parent": "OWNER DATA" },    // ... 更多数据];

期望的输出结构:

{  "REPORTING PERIOD": "2022",  "SIGNATURE DATE": "20211005",  "HOUSE": {    "OWNER DATA": {      "FIRST NAME": "Joe",      "LAST NAME": "Smith"    },    // ... 其他 HOUSE 内部的嵌套对象  },  // ... 其他顶级对象}

显而易见,挑战在于如何动态地创建和引用这些嵌套层级,确保每个子元素都能正确地放置在其父元素之下。

传统迭代方法的局限性

一种直观的尝试是遍历数组,根据parent字段将元素添加到结果对象中。

const resultObject = {};for (const obj of data) {    const { key, value, parent } = obj;    if (parent === "" || parent === undefined) {      resultObject[key] = value; // 顶级元素    } else {      // 如果父级不存在,创建一个空对象      if (!resultObject[parent]) {        resultObject[parent] = {};      }      // 将当前元素作为子属性添加到父级对象中      (resultObject[parent])[key] = value;    }}console.log(resultObject);

这种方法的问题在于,它只能正确处理一级嵌套。例如,”FIRST NAME”的父级是”OWNER DATA”,而”OWNER DATA”的父级是”HOUSE”。上述代码会将”FIRST NAME”直接添加到resultObject[“OWNER DATA”]中,但resultObject[“OWNER DATA”]本身可能还未被正确地嵌套到resultObject[“HOUSE”]中,或者resultObject[“OWNER DATA”]可能被错误地初始化为 {} 而不是作为HOUSE的子属性。它无法在一次循环中动态地追踪和构建任意深度的嵌套路径。

解决方案:利用 Array.prototype.reduce 和 ??=

要高效地处理多层级嵌套,我们可以使用 Array.prototype.reduce 方法。reduce允许我们遍历数组并累积一个单一的结果。关键在于,我们可以利用reduce的累加器(accumulator)来同时作为我们构建的目标对象和查找父级节点的映射表。

核心思路:

累加器作为映射表: reduce的累加器 (a) 将不仅存储最终的嵌套对象,还将存储所有已处理的父节点(包括中间节点)的引用。这使得我们可以在处理子节点时,快速找到其父节点的实际引用。空值合并赋值 (??=): 这个运算符 (a.property ??= value) 可以在 a.property 为 null 或 undefined 时,将其赋值为 value,并返回 a.property 的值。这极大地简化了“如果不存在则创建”的逻辑。处理中间父节点: 当一个元素的value为空字符串时,它通常表示一个中间的父节点(如”HOUSE”、”OWNER DATA”)。对于这类节点,我们不直接赋值其value,而是确保它在累加器中是一个对象,以便后续的子节点可以添加到其中。最终结果提取: 我们的顶级节点(parent: “”)会最终累积到累加器中的 a[”] 键下,所以最终结果就是 a[”]。

示例代码:

const data = [    {"rank":0,"key":"REPORTING PERIOD","value":"2022","parent":""},    {"rank":0,"key":"SIGNATURE DATE","value":"20211005","parent":""},    {"rank":0,"key":"HOUSE","value":"","parent":""},    {"rank":1,"key":"OWNER DATA","value":"","parent":"HOUSE"},    {"rank":2,"key":"FIRST NAME","value":"Joe","parent":"OWNER DATA"},    {"rank":2,"key":"LAST NAME","value":"Smith","parent":"OWNER DATA"},    {"rank":1,"key":"VALUE HISTORY","value":"","parent":"HOUSE"},    {"rank":2,"key":"INITAL PRICE","value":"12345","parent":"VALUE HISTORY"},    {"rank":2,"key":"LAST SALE PRICE","value":"1231236","parent":"VALUE HISTORY"},    {"rank":1,"key":"ADDRESS","value":"","parent":"HOUSE"},    {"rank":2,"key":"STREET 1","value":"5 MAIN TERRACE","parent":"ADDRESS"},    {"rank":2,"key":"CITY","value":"LONDON","parent":"ADDRESS"},    {"rank":0,"key":"AGENT","value":"","parent":""},    {"rank":1,"key":"COMPANY DATA","value":"","parent":"AGENT"},    {"rank":2,"key":"COMPANY NAME","value":"The Real Agent, Inc","parent":"COMPANY DATA"},    {"rank":2,"key":"BUSINESS NUMBER","value":"0021690080","parent":"COMPANY DATA"},    {"rank":1,"key":"BUSINESS ADDRESS","value":"","parent":"AGENT"},    {"rank":2,"key":"STREET 1","value":"800 MENLO STREET, SUITE 100","parent":"BUSINESS ADDRESS"},    {"rank":2,"key":"CITY","value":"MENLO PARK","parent":"BUSINESS ADDRESS"},    {"rank":2,"key":"ZIP","value":"94025","parent":"BUSINESS ADDRESS"}];const tree = data.reduce((accumulator, { parent, key, value }) => {  // 1. 确保父节点存在于累加器中,如果不存在则初始化为 {}  // (accumulator[parent] ??= {}) 会返回 accumulator[parent] 的引用  // 2. 在获取到的父节点引用上,设置当前 key 的值  // 3. 如果当前 value 为空字符串,表示它是一个中间父节点,  //    则将其值设置为一个新的空对象 (accumulator[key] ??= {}),  //    以便后续的子节点可以添加到这个对象中。  //    否则,直接赋值其 value。  (accumulator[parent] ??= {})[key] = value === '' ? (accumulator[key] ??= {}) : value;  return accumulator; // 返回更新后的累加器}, {})['']; // 初始累加器为空对象,并最终取出 'parent: ""' 对应的顶级结构// 注意:最后的 [''] 是为了从累加器中提取最终的根对象,// 因为所有顶级元素 (parent 为 "") 都会被归类到 accumulator[''] 中。console.log(JSON.stringify(tree, null, 2));

代码解析:

data.reduce((accumulator, { parent, key, value }) => { … }, {}):reduce方法遍历data数组。accumulator(简写为a)是累加器,它在每次迭代中传递并累积结果。初始值是一个空对象{}。{ parent, key, value }是解构赋值,从当前处理的数组元素中提取这三个属性。(accumulator[parent] ??= {}):这是整个解决方案的核心。它首先尝试访问 accumulator[parent]。如果 accumulator[parent] 是 null 或 undefined(即这个父节点是第一次出现),??= 运算符会将其赋值为一个新的空对象 {}。无论 accumulator[parent] 原本是否存在,这个表达式都会返回 accumulator[parent] 的引用。这意味着我们现在有了一个指向正确父级对象的引用,无论是新创建的还是已存在的。[key] = value === ” ? (accumulator[key] ??= {}) : value;:在获取到的父节点引用上,我们设置 key 属性的值。value === ” ? (accumulator[key] ??= {}) : value 是一个三元表达式:如果 value 为空字符串:这表示当前的key(例如”HOUSE”或”OWNER DATA”)本身是一个中间父节点,它应该包含其他子节点。因此,我们不能直接给它赋值一个空字符串。相反,我们确保 accumulator[key] 也是一个对象 (accumulator[key] ??= {}),并将其引用赋值给父节点下的 key。这样做的好处是,accumulator[key]这个新的对象引用也会被存入到全局的accumulator中,以便后续的子节点可以直接找到它并添加到它下面。如果 value 不为空字符串:这表示当前的key是一个叶子节点(例如”FIRST NAME”),直接将其value赋值给它即可。return accumulator;:在每次迭代结束时,返回更新后的累加器,以便下一次迭代使用。[”]:reduce方法执行完毕后,accumulator对象会包含所有中间和最终的节点引用。由于所有顶级元素(parent为空字符串)都最终被归类到 accumulator[”] 之下,因此通过 [”] 我们可以直接获取到最终的、完整的嵌套对象结构。

注意事项与最佳实践

parent字段的规范性: 确保parent字段的值与某个key字段的值精确匹配。任何拼写错误或不一致都可能导致节点无法正确归属。循环引用: 这种方法假设数据中不存在循环引用(即A是B的父,B是A的父)。如果存在循环引用,可能会导致无限递归或意外行为。性能: reduce方法在一次遍历中完成所有操作,对于大多数数据集来说效率很高。对于极大的数据集,其性能表现良好,因为避免了多次查找和不必要的对象创建。rank字段的用途: 在原始数据中存在rank字段,但在本解决方案中并未直接使用。如果rank字段有特定的业务逻辑需求(例如排序或验证),可能需要在reduce逻辑中加入额外的处理。

总结

通过巧妙地运用 Array.prototype.reduce 方法和 nullish coalescing assignment (??=) 运算符,我们可以高效且简洁地将一个扁平化的对象数组转换为任意深度的嵌套对象。这种模式在处理具有父子关系的数据转换时非常强大和灵活,是JavaScript开发中一个值得掌握的技巧。理解其工作原理不仅能解决当前问题,还能为处理其他复杂数据结构转换提供思路。

以上就是将扁平对象数组转换为多层级嵌套对象的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1529434.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
React Hook Form 动态输入字段处理指南
上一篇 2025年12月20日 21:07:30
优化 React Native 日期选择器:避免重复请求和数据更新
下一篇 2025年12月20日 21:07:46

相关推荐

  • Java 正则表达式:查找双引号内所有指定字符串的出现次数

    本文旨在解决在 Java 中使用正则表达式查找双引号内特定字符串(例如 “variant”)的所有出现次数的问题。我们将提供一个完整的解决方案,包括正则表达式的构建、代码示例以及详细的解释,帮助开发者准确高效地完成此类任务。 在 Java 中,使用正则表达式查找字符串中特定模…

    2026年9月21日
    000
  • MySQL 大型历史数据表结构设计与优化指南

    本文旨在为处理大量客户历史交易数据的MySQL数据库设计提供专业指导。我们将探讨如何构建高效、可扩展的表结构,重点关注主键设计、数据分区、实时数据摄入以及性能优化策略,以确保系统能够稳定支持百万级乃至亿级数据量的查询需求。 MySQL大型历史数据表结构设计与优化 在处理大量历史数据,特别是涉及到多用…

    2026年9月21日
    000
  • MySQL重复数据检测与清理逻辑_Sublime脚本批量处理历史冗余记录

    MySQL重复数据检测与清理逻辑_Sublime脚本批量处理历史冗余记录MySQL重复数据检测与清理逻辑_Sublime脚本批量处理历史冗余记录MySQL重复数据检测与清理逻辑_Sublime脚本批量处理历史冗余记录MySQL重复数据检测与清理逻辑_Sublime脚本批量处理历史冗余记录

    处理mysql重复数据的核心步骤是识别并清理,可使用group by或窗口函数定位重复项,再通过分批删除或倒腾法安全清理;sublime text可用于高效生成和编辑sql语句。1. 识别重复数据常用group by+having或row_number()窗口函数;2. 清理策略包括分批删除、使用临…

    2026年9月21日 • 用户投稿
    100
  • 如何用PyTorch训练AI大模型?构建高效神经网络的完整教程

    如何用PyTorch训练AI大模型?构建高效神经网络的完整教程如何用PyTorch训练AI大模型?构建高效神经网络的完整教程如何用PyTorch训练AI大模型?构建高效神经网络的完整教程如何用PyTorch训练AI大模型?构建高效神经网络的完整教程

    PyTorch大模型训练需综合运用分布式训练、内存优化与高效计算策略。首先采用DistributedDataParallel实现多GPU并行,配合DistributedSampler确保数据均衡;通过混合精度训练、梯度累积和激活检查点缓解显存压力;使用torch.compile优化模型计算效率;选择…

    2026年9月21日 • 用户投稿
    000
  • MySQL自动化性能测试方案_MySQL持续监控调优数据库效率

    MySQL自动化性能测试方案_MySQL持续监控调优数据库效率MySQL自动化性能测试方案_MySQL持续监控调优数据库效率MySQL自动化性能测试方案_MySQL持续监控调优数据库效率MySQL自动化性能测试方案_MySQL持续监控调优数据库效率

    mysql自动化性能测试和持续监控的核心在于构建闭环反馈系统,包含模拟真实负载、全面数据采集、自动化执行与分析、数据驱动的持续调优四大环节。①测试环境需与生产一致并隔离,使用docker、虚拟机或云沙盒,解决数据同步与脱敏问题;②负载生成工具如sysbench、jmeter、locust或自定义脚本…

    2026年9月21日 • 用户投稿
    200
  • CyberLinkMediaSuite如何制作AI视频?多功能工具快速剪辑的方法

    CyberLinkMediaSuite如何制作AI视频?多功能工具快速剪辑的方法CyberLinkMediaSuite如何制作AI视频?多功能工具快速剪辑的方法CyberLinkMediaSuite如何制作AI视频?多功能工具快速剪辑的方法CyberLinkMediaSuite如何制作AI视频?多功能工具快速剪辑的方法

    答案:CyberLink MediaSuite(核心为PowerDirector)通过AI艺术风格转换、智能对象选取、AI天空替换、音频降噪与运动追踪等功能,显著提升视频制作效率与创意表现。结合模板应用、快捷键操作、媒体库管理及代理编辑等实战技巧,可实现快速剪辑与专业输出,适用于Vlog创作、教育视…

    2026年9月21日 • 用户投稿
    300
  • Win10与Ubuntu 18.04双系统安装。(Win10引导Linux)[通俗易懂]

    Win10与Ubuntu 18.04双系统安装。(Win10引导Linux)[通俗易懂]Win10与Ubuntu 18.04双系统安装。(Win10引导Linux)[通俗易懂]Win10与Ubuntu 18.04双系统安装。(Win10引导Linux)[通俗易懂]Win10与Ubuntu 18.04双系统安装。(Win10引导Linux)[通俗易懂]

    大家好,很高兴再次与大家见面,我是你们的老朋友全栈君。 作为一个初学者,为了满足自己的求知欲,我按照几位大神写的教程尝试了一遍安装过程,现在来和大家分享一下。 1、Win10安装(如果已经安装,请跳过) 1)制作系统U盘(参考微信公众号“软件安装管家”): https://www.php.cn/li…

    2026年9月21日 • 用户投稿
    400
  • 百家号视频怎么隐藏?百家号怎么设置仅自己可见

    随着短视频平台的快速发展,其已成为人们获取资讯和休闲娱乐的重要方式。作为国内知名的自媒体平台之一,百家号吸引了大量用户。然而,在享受便捷的同时,隐私安全问题也日益突出。本文将介绍百家号视频隐藏的方法,帮助用户更好地保护个人内容,维护隐私安全。 一、百家号视频隐藏方法 设置隐私权限 在百家号后台,用户…

    2026年9月21日
    100
  • MySQL数据库如何设计适合大数据量的表结构_案例分析?

    MySQL数据库如何设计适合大数据量的表结构_案例分析?MySQL数据库如何设计适合大数据量的表结构_案例分析?MySQL数据库如何设计适合大数据量的表结构_案例分析?MySQL数据库如何设计适合大数据量的表结构_案例分析?

    设计适合大数据量的mysql表结构,核心在于数据类型选对、索引用好、适当拆分。1. 合理选择字段类型,如根据数据范围选用tinyint/smallint代替bigint,固定值字段用enum类型,大文本字段单独拆表;2. 精准建立索引,高频查询字段建联合索引并遵循最左前缀原则,避免低区分度字段建索引…

    2026年9月21日 • 用户投稿
    100
  • Java Random类如何生成随机数

    Random类位于java.util包,通过实例化生成伪随机数;无参构造以系统时间作种子,带参构造用固定种子可复现序列;提供nextInt()、nextDouble()等方法生成不同类型随机值;指定范围整数可用rand.nextInt(max-min)+min实现;多线程推荐ThreadLocalR…

    2026年9月21日
    200
  • windows10如何查看S.M.A.R.T.硬盘状态_windows10硬盘S.M.A.R.T.状态查看方法

    电脑运行慢、蓝屏或文件损坏可能是硬盘故障前兆,可通过S.M.A.R.T.技术检测健康状况。1、使用WMIC命令行工具输入“wmic diskdrive get model,status”查看状态,显示Pred Fail需立即备份数据;2、CrystalDiskInfo可深度分析S.M.A.R.T.参…

    2026年9月21日
    200
  • Photopea的AI功能怎么裁剪图片?快速实现高效图片裁剪技巧

    Photopea的AI功能怎么裁剪图片?快速实现高效图片裁剪技巧Photopea的AI功能怎么裁剪图片?快速实现高效图片裁剪技巧Photopea的AI功能怎么裁剪图片?快速实现高效图片裁剪技巧Photopea的AI功能怎么裁剪图片?快速实现高效图片裁剪技巧

    Photopea的AI功能通过智能选择工具与内容感知技术结合,实现高效图片裁剪。首先使用对象选择、快速选择或魔棒工具智能识别主体或背景,再通过“选择并遮住”精细调整边缘,尤其适用于复杂轮廓如发丝。随后可应用图层蒙版透明化背景,并用裁剪工具调整画布范围。结合内容感知填充可移除干扰元素并自动补全画面,内…

    2026年9月21日 • 用户投稿
    300
  • PHP框架中间件有什么用处_PHP框架中间件设计与实现

    PHP框架中间件是处理请求和响应的过滤器,用于实现身份验证、日志记录、CORS等通用逻辑,核心价值在于解耦和提升可维护性。通过定义中间件接口、具体中间件类及管道调度器可实现自定义中间件,如身份验证或CORS处理。在Laravel中可通过Kernel.php配置全局、分组或路由级中间件,执行顺序按注册…

    2026年9月21日
    100
  • Java中字符到数字转换:解决for循环提前返回的常见陷阱

    本文探讨java中`for`循环在字符到数字转换时,因`return`语句放置不当导致程序提前终止、无法完整处理字符串的问题。我们将分析这种常见陷阱,并提供修正方案,演示如何正确利用循环填充数组,并在循环结束后统一返回最终结果,确保每个字符都能被准确映射和组合。 引言:字符到数字的映射需求 在编程实…

    2026年9月21日
    100
  • 梦幻号虚拟主播电商运营宝典(附新手教程+配套工具清单)

    虚拟主播电商的核心在于“内容驱动销售,人设凝聚用户”,要让“梦幻号”真正动起来并实现带货,必须先赋予其鲜明的人设,包括清晰的定位标签(如美食家、科技宅)、独特的人格魅力(性格、口头禅、小缺点)和与产品的强关联性,使其具备辨识度和故事感,从而建立用户信任;接着通过obs studio、vtube st…

    2026年9月21日
    000
  • deepseek下载速度优化_从deepseek下载速度优化官网获取

    deepseek下载速度优化入口在官网https://www.deepseek.com,进入后可通过设置调整响应模式、使用智能路由和数据压缩技术提升速度。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ deepseek下载速度优化入口地址在…

    2026年9月21日
    100
  • Java多线程API调用中Future.get()返回null的解决方案

    本文旨在解决%ignore_a_1%api调用中`future.get()`方法返回`null`的常见问题。当使用`callable`和`executorservice`并发执行api请求并尝试获取结果时,如果流读取逻辑不当,可能导致获取到的数据为空。文章将详细解释问题根源,并提供使用`string…

    2026年9月21日
    100
  • 升级后如何检查兼容性

    检查兼容性是升级后确保系统稳定的关键,需先确认硬件配置与驱动支持,再验证软件运行及业务流程正常,最后通过系统日志排查潜在错误,逐步排除风险。 系统或软件升级后,检查兼容性是确保各项功能正常运行的关键步骤。直接进入实际使用前,花时间验证兼容性可以避免数据丢失、服务中断等问题。 检查硬件和驱动支持 某些…

    2026年9月21日
    100
  • mysql如何排查排序异常

    排查MySQL排序异常需先确认ORDER BY是否生效,检查子查询、UNION及应用层逻辑是否覆盖排序;通过EXPLAIN分析是否使用索引排序,避免Using filesort;确保字段类型、字符集和排序规则(collation)符合预期,处理NULL值和大小写敏感性;关注sort_buffer_s…

    2026年9月21日
    000
  • 即梦AI运镜控制怎么控制_即梦AI视频镜头移动技巧详解

    掌握即梦AI运镜需四步:一、用“镜头缓慢推进”等预设提示词生成标准运动;二、通过动效画板框选主体并绘制运动路径;三、设置首尾帧引导转场,实现穿越或循环效果;四、结合“希区柯克式变焦”“时间冻结环绕”等高级技巧增强视觉表现。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 Dee…

    2026年9月21日
    000

发表回复

登录后才能评论
关注微信