0

0

使用JavaScript的reduce方法进行复杂数组对象转换与聚合

聖光之護

聖光之護

发布时间:2025-11-11 20:47:01

|

155人浏览过

|

来源于php中文网

原创

使用JavaScript的reduce方法进行复杂数组对象转换与聚合

本文深入探讨如何利用javascript的`array.prototype.reduce()`方法,将一个扁平的对象数组转换为具有多层嵌套和数据聚合的新结构。通过一个具体的医疗数据转换案例,详细解析`reduce`的工作原理,包括累加器初始化、条件查找与更新,以及如何构建复杂的嵌套对象,从而实现高效且声明式的数据重塑。

理解数据转换需求

在现代Web开发中,我们经常需要对数据进行重塑以满足特定的展示或处理需求。假设我们有一个包含医疗服务记录的扁平数组,每个对象都包含medico(医生)、rateio(费率类型)、convenio(协议)和subtotal(小计)等信息。

原始数据结构示例:

const arr = [
  { medico: "med1", rateio: "rat1", convenio: "conv1", subtotal: 10 },
  { medico: "med2", rateio: "rat2", convenio: "conv2", subtotal: 10 },
  { medico: "med2", rateio: "rat2", convenio: "conv2", subtotal: 20 },
  // ... 更多数据
];

我们的目标是将这个扁平数组转换为一个高度结构化的嵌套对象数组,其中数据按medico分组,medico下按rateio分组,rateio下再按convenio分组,并且对相同convenio的subtotal进行求和。

目标数据结构示例:

立即学习Java免费学习笔记(深入)”;

const result = [
  {
    medico: "med1",
    grantotals: [
      {
        rateio: "rat1",
        grandtotals: [
          { convenio: "conv1", sum_subtotal: 10 },
          { convenio: "conv3", sum_subtotal: 45 }
        ]
      }
    ]
  },
  // ... 更多数据
];

这种转换要求我们不仅要进行分组,还要在分组过程中对数值进行累加,并且创建多层嵌套的结构。

一点PPT
一点PPT

一句话生成专业PPT,AI自动排版配图

下载

运用 Array.prototype.reduce() 进行复杂转换

Array.prototype.reduce() 方法是JavaScript中一个非常强大的高阶函数,它对数组中的每个元素执行一个由您提供的“reducer”函数,将其结果汇总为单个返回值。这使得它非常适合用于构建复杂的数据结构,例如我们这里需要的嵌套聚合对象。

reduce 方法的语法如下: arr.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue)

  • accumulator:累加器,它会记住每次回调函数执行的返回值,并在下一次执行时作为第一个参数传入。
  • currentValue:当前正在处理的数组元素。
  • initialValue:可选参数,作为第一次调用 callback 函数时的 accumulator 值。如果未提供,则 arr 的第一个元素将作为 accumulator,并且 currentValue 将从 arr 的第二个元素开始。

在我们的场景中,accumulator 将逐步构建出最终的嵌套结果数组。

逐步构建嵌套结构

我们将通过一个详细的reduce实现来展示如何达到目标结构。核心思想是:对于数组中的每一个原始对象,我们检查其各个层级的属性(medico, rateio, convenio)是否已存在于累加器中。如果存在,则更新相应的数据;如果不存在,则创建新的层级结构。

const arr = [
    { medico: "med1", rateio: "rat1", convenio: "conv1", subtotal: 10 },
    { medico: "med2", rateio: "rat2", convenio: "conv2", subtotal: 10 },
    { medico: "med2", rateio: "rat2", convenio: "conv2", subtotal: 20 },
    { medico: "med1", rateio: "rat1", convenio: "conv3", subtotal: 20 },
    { medico: "med1", rateio: "rat1", convenio: "conv3", subtotal: 25 },
    { medico: "med2", rateio: "rat3", convenio: "conv4", subtotal: 15 },
    { medico: "med2", rateio: "rat4", convenio: "conv3", subtotal: 10 },
];

const result = arr.reduce((acc, obj) => {
    // 1. 查找或创建顶层 medico 对象
    let existingMedico = acc.find((item) => item.medico === obj.medico);

    if (!existingMedico) {
        // 如果 medico 不存在,则创建新的 medico 对象并添加到累加器
        existingMedico = {
            medico: obj.medico,
            grantotals: [], // 初始化 grantotals 数组
        };
        acc.push(existingMedico);
    }

    // 2. 在找到或创建的 medico 对象中,查找或创建 rateio 对象
    let existingRateio = existingMedico.grantotals.find(
        (item) => item.rateio === obj.rateio
    );

    if (!existingRateio) {
        // 如果 rateio 不存在,则创建新的 rateio 对象并添加到 medico 的 grantotals
        existingRateio = {
            rateio: obj.rateio,
            grandtotals: [], // 初始化 grandtotals 数组
        };
        existingMedico.grantotals.push(existingRateio);
    }

    // 3. 在找到或创建的 rateio 对象中,查找或创建 convenio 对象并聚合 subtotal
    let existingConvenio = existingRateio.grandtotals.find(
        (item) => item.convenio === obj.convenio
    );

    if (existingConvenio) {
        // 如果 convenio 存在,则更新其 sum_subtotal
        existingConvenio.sum_subtotal += obj.subtotal;
    } else {
        // 如果 convenio 不存在,则创建新的 convenio 对象并添加到 rateio 的 grandtotals
        existingRateio.grandtotals.push({
            convenio: obj.convenio,
            sum_subtotal: obj.subtotal,
        });
    }

    return acc; // 返回更新后的累加器
}, []); // 初始累加器为空数组

console.log(JSON.stringify(result, null, 2));

代码逻辑详解:

  1. 初始化累加器 (acc):reduce方法的第二个参数[]将acc初始化为一个空数组,它将最终存储转换后的数据。
  2. 遍历每个原始对象 (obj):对于arr中的每一个元素,reduce回调函数都会执行。
  3. 处理 medico 层级
    • 首先,在当前的acc中查找是否存在与obj.medico匹配的medico对象。
    • 如果不存在,则创建一个新的medico对象,并初始化其grantotals数组,然后将其推入acc。
    • 如果存在,则直接使用找到的medico对象。
  4. 处理 rateio 层级
    • 在当前existingMedico对象的grantotals数组中,查找是否存在与obj.rateio匹配的rateio对象。
    • 如果不存在,则创建一个新的rateio对象,并初始化其grandtotals数组,然后将其推入existingMedico.grantotals。
    • 如果存在,则直接使用找到的rateio对象。
  5. 处理 convenio 层级及聚合 subtotal
    • 在当前existingRateio对象的grandtotals数组中,查找是否存在与obj.convenio匹配的convenio对象。
    • 如果存在,则直接将obj.subtotal的值累加到existingConvenio.sum_subtotal上。
    • 如果不存在,则创建一个新的convenio对象,包含obj.convenio和obj.subtotal(作为初始sum_subtotal),然后将其推入existingRateio.grandtotals。
  6. 返回 acc:每次迭代结束后,reduce回调函数必须返回更新后的acc,以便在下一次迭代中使用。

注意事项与性能考量

  • 嵌套 find 的性能:在上述解决方案中,我们使用了多层嵌套的find方法来查找现有对象。对于小型数组,这通常不是问题。但如果原始数组arr非常大,或者嵌套层级很深,频繁的find操作(其时间复杂度为O(n))可能导致整体性能下降,因为每次查找都需要遍历子数组。
  • 替代方案:对于性能敏感的场景,可以考虑使用Map或Object作为查找表来优化查找过程,将查找时间复杂度降至O(1)。例如,可以维护一个Map<medico, medicoObject>,一个Map<rateio, rateioObject>等。然而,这会增加代码的复杂性,并且可能需要多步reduce或更复杂的单步reduce逻辑。
  • 代码可读性:虽然reduce非常强大,但当逻辑变得非常复杂时(如本例中的多层嵌套条件判断),代码的可读性可能会降低。适当地拆分逻辑或添加注释可以帮助维护。
  • Immutability (不变性):本例中的reduce实现通过直接修改acc及其内部对象的属性来构建结果。这在某些情况下是可接受的,但如果需要严格遵循不变性原则,则每次更新都应创建新的对象和数组副本,这会使代码更加复杂,并可能对性能产生额外开销。

总结

Array.prototype.reduce()是JavaScript中一个功能强大的数组方法,它能够将一个数组转换为任何你想要的单一值或复杂数据结构。通过精心设计的累加器逻辑,我们可以实现多层分组、数据聚合和结构重塑等复杂任务。尽管在处理大规模数据时需要考虑性能优化,但对于大多数场景,reduce提供了一种简洁、声明式且高效的方式来处理数据转换需求。理解其工作原理和灵活运用是每个JavaScript开发者必备的技能。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

550

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

30

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

45

2026.01.06

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

77

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

40

2025.11.16

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

67

2025.11.17

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

47

2025.11.27

PHP 高并发与性能优化
PHP 高并发与性能优化

本专题聚焦 PHP 在高并发场景下的性能优化与系统调优,内容涵盖 Nginx 与 PHP-FPM 优化、Opcode 缓存、Redis/Memcached 应用、异步任务队列、数据库优化、代码性能分析与瓶颈排查。通过实战案例(如高并发接口优化、缓存系统设计、秒杀活动实现),帮助学习者掌握 构建高性能PHP后端系统的核心能力。

114

2025.10.16

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号