0

0

JavaScript reduce 高级用法:多层级数据结构转换与汇总

碧海醫心

碧海醫心

发布时间:2025-11-11 19:11:21

|

881人浏览过

|

来源于php中文网

原创

JavaScript reduce 高级用法:多层级数据结构转换与汇总

本文详细阐述了如何运用 javascript 的 `reduce` 方法对复杂对象数组进行深度转换与聚合。教程通过一个具体示例,展示了如何逐层构建嵌套结构,并根据 `medico`、`rateio` 和 `convenio` 等键对数据进行分组及 `subtotal` 求和,以实现高效且结构化的数据重塑。

JavaScript reduce 方法基础

在深入探讨复杂数据转换之前,我们首先回顾一下 Array.prototype.reduce() 方法。reduce() 方法对数组中的每个元素执行一个由您提供的 reducer 函数,将其结果汇总为单个返回值。

reducer 函数接收四个参数:

  • accumulator (累加器):上一次调用 reducer 函数的返回值,或者是 initialValue。
  • currentValue (当前值):数组中正在处理的当前元素。
  • currentIndex (当前索引):数组中正在处理的当前元素的索引。
  • array (源数组):调用 reduce() 的数组本身。

reduce() 方法通常用于将数组扁平化、计算总和、构建新对象等场景,其灵活性使其成为处理复杂数据转换的强大工具

复杂数据转换需求分析

假设我们有一个扁平化的对象数组,其中包含 medico(医生)、rateio(费率)、convenio(协议)和 subtotal(小计)等信息:

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

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 },
];

我们的目标是将这个扁平数组转换为一个高度结构化的嵌套数组,具体要求如下:

  1. 按 medico 分组:最外层以 medico 为键进行分组。
  2. 按 rateio 分组:在每个 medico 组下,进一步按 rateio 分组。
  3. 按 convenio 分组并汇总 subtotal:在每个 rateio 组下,按 convenio 分组,并将其对应的 subtotal 值进行累加,形成 sum_subtotal。

期望的输出结构如下:

const result = [
  {medico: "med1", grantotals:[
    {
      rateio: "rat1",
      grandtotals: [
        { convenio: "conv1", sum_subtotal: 10 },
        { convenio: "conv3", sum_subtotal: 45 } // 20 + 25
      ]
    }
  ]},
  {medico: "med2", grantotals:[
      {
        rateio: "rat2",
        grandtotals: [
          { convenio: "conv2", sum_subtotal: 30 }, // 10 + 20
        ]
      },
      {
        rateio: "rat3",
        grandtotals: [
          { convenio: "conv4", sum_subtotal: 15 },
        ]
      },
      {
        rateio: "rat4",
        grandtotals: [
          { convenio: "conv3", sum_subtotal: 10 },
        ]
      }
    ]}
];

使用 reduce 实现层级聚合

为了实现上述复杂转换,我们将利用 reduce 方法的累加器来逐步构建目标结构。核心思路是:对于原始数组中的每一个元素,我们都尝试在累加器中查找其对应的 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: "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) {
        existingMedico = {
            medico: obj.medico,
            grantotals: [],
        };
        acc.push(existingMedico);
    }

    // 2. 在 medico 层级下查找或创建 rateio 层级
    let existingRateio = existingMedico.grantotals.find(
        (item) => item.rateio === obj.rateio
    );

    if (!existingRateio) {
        existingRateio = {
            rateio: obj.rateio,
            grandtotals: [],
        };
        existingMedico.grantotals.push(existingRateio);
    }

    // 3. 在 rateio 层级下查找或创建 convenio 层级并更新 subtotal
    let existingConvenio = existingRateio.grandtotals.find(
        (item) => item.convenio === obj.convenio
    );

    if (existingConvenio) {
        existingConvenio.sum_subtotal += obj.subtotal;
    } else {
        existingRateio.grandtotals.push({
            convenio: obj.convenio,
            sum_subtotal: obj.subtotal,
        });
    }

    return acc;
}, []);

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

代码逐行解析

  1. arr.reduce((acc, obj) => { ... }, []);

    • reduce 方法被调用在原始数组 arr 上。
    • acc 是累加器,初始化为一个空数组 [],它将逐步构建成最终的目标结构。
    • obj 是 arr 中当前正在处理的元素。
  2. 查找或创建 medico 层级

    Paraflow
    Paraflow

    AI产品设计智能体

    下载
    let existingMedico = acc.find((item) => item.medico === obj.medico);
    if (!existingMedico) {
        existingMedico = { medico: obj.medico, grantotals: [] };
        acc.push(existingMedico);
    }
    • acc.find(...):尝试在累加器 acc(即已构建的 medico 列表)中查找当前 obj.medico 是否已存在。
    • 如果 existingMedico 为 null 或 undefined(表示未找到),则创建一个新的 medico 对象,并将其 grantotals 数组初始化为空,然后将此新对象推入 acc。
    • 无论找到还是创建,existingMedico 都会指向当前 medico 的对象引用,以便后续操作。
  3. 在 medico 层级下查找或创建 rateio 层级

    let existingRateio = existingMedico.grantotals.find(
        (item) => item.rateio === obj.rateio
    );
    if (!existingRateio) {
        existingRateio = { rateio: obj.rateio, grandtotals: [] };
        existingMedico.grantotals.push(existingRateio);
    }
    • 逻辑与 medico 层级类似。在 existingMedico.grantotals 数组中查找 obj.rateio。
    • 如果未找到,则创建一个新的 rateio 对象,初始化其 grandtotals 数组,并推入 existingMedico.grantotals。
  4. 在 rateio 层级下查找或创建 convenio 层级并更新 subtotal

    let existingConvenio = existingRateio.grandtotals.find(
        (item) => item.convenio === obj.convenio
    );
    if (existingConvenio) {
        existingConvenio.sum_subtotal += obj.subtotal;
    } else {
        existingRateio.grandtotals.push({
            convenio: obj.convenio,
            sum_subtotal: obj.subtotal,
        });
    }
    • 在 existingRateio.grandtotals 数组中查找 obj.convenio。
    • 如果找到:说明该 convenio 已存在,直接将其 sum_subtotal 属性加上当前 obj.subtotal。
    • 如果未找到:创建一个新的 convenio 对象,设置其 convenio 和 sum_subtotal(初始值为 obj.subtotal),然后将其推入 existingRateio.grandtotals。
  5. return acc;

    • 在每次迭代结束时,返回更新后的累加器 acc,它将作为下一次迭代的 accumulator。

性能考量与优化方案

上述 reduce 实现虽然功能正确且易于理解,但在处理大型数据集时可能会遇到性能瓶颈。主要原因是使用了 Array.prototype.find() 方法进行层层查找。find() 方法的时间复杂度为 O(N),在最坏情况下,每次迭代都需要遍历数组。如果 medico、rateio、convenio 的种类繁多,整个转换过程的复杂度将显著增加。

为了优化性能,我们可以考虑使用 JavaScript 的 Map 对象作为中间缓存,将查找操作的时间复杂度从 O(N) 降低到 O(1)。

基于 Map 的优化思路:

  1. 主累加器仍是数组: 最终结果仍是一个数组。
  2. 引入 Map 进行快速查找: 在 reduce 的累加器中,除了最终结果数组,还可以维护一个或多个 Map 对象,用于存储已处理的 medico、rateio、convenio 对象的引用,以其唯一标识符作为键。
    • 例如,可以有一个 medicoMap,键是 medico 名称,值是对应的 medico 对象。
    • 类似地,每个 medico 对象内部可以维护一个 rateioMap,每个 rateio 对象内部维护一个 convenioMap。
  3. 查找逻辑: 每次处理 obj 时,首先通过 medicoMap.get(obj.medico) 快速查找。如果不存在,则创建新 medico 对象,添加到结果数组,并更新 medicoMap。
  4. 嵌套查找: 对于 rateio 和 convenio,也采用相同的方式,利用它们各自的 Map 进行 O(1) 查找和更新。

这种 Map 优化的方法可以显著提升处理大量数据的效率,尤其是在唯一键值较多的情况下。

总结

Array.prototype.reduce() 是 JavaScript 中一个极其强大的数组方法,能够实现从简单的聚合到复杂的数据结构转换。通过本教程,我们学习了如何利用 reduce 方法,结合条件判断和层级查找,将一个扁平化的对象数组转换为多层级嵌套并带有聚合计算的复杂结构。

在实际应用中,理解 reduce 的工作原理及其在不同场景下的适用性至关重要。同时,对于大型数据集,我们也应关注性能问题,并考虑使用 Map 等数据结构进行优化,以确保代码的效率和可扩展性。掌握这些技巧,将使您在处理 JavaScript 数据转换任务时更加得心应手。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

438

2024.03.01

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

183

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

286

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

258

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

124

2025.08.07

treenode的用法
treenode的用法

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

538

2023.12.01

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

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

17

2025.12.22

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

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

精品课程

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

共58课时 | 4.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

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

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