深度解析:JavaScript中如何按层级汇总嵌套数据结构中的金额

深度解析:javascript中如何按层级汇总嵌套数据结构中的金额

本文详细阐述了如何在多层嵌套的树形数据结构中,按层级精确计算并汇总每个层级的存款总额。通过递归遍历和层级聚合的策略,提供了一种高效且结构清晰的JavaScript解决方案,适用于处理如推荐系统、组织架构等场景中的分层数据,确保能准确获取每个层级的独立总和,而非扁平化的所有存款列表。

问题背景与数据结构

在许多业务场景中,我们经常会遇到具有层级关系的数据,例如公司的组织架构、用户的推荐系统(多级下线)、文件目录等。这些数据通常以嵌套的JSON对象或数组形式表示。本教程将以一个典型的用户层级存款数据为例,演示如何计算每个层级的存款总额。

假设我们有一个表示用户及其下线的层级结构,每个用户节点包含一个deposit字段表示其存款金额,以及一个可选的children数组,表示其直接下线。层级深度可能不固定,但通常会有最大限制(例如,最多5层)。我们的目标是得到一个数组,其中每个元素代表对应层级的存款总和。

以下是示例数据结构片段:

[    {        "id": "ddf86d60-a607-4a4e-a7f9-d96013ee7070",        "name": "Rick Rich",        "deposit": 100,        "children": [            {                "id": "25de2e98-eb2d-41f4-b225-3069f942b284",                "name": "Rick Rich",                "deposit": 100,                "children": [                    {                        "id": "376b202e-d44f-4402-9560-8498c855d05e",                        "name": "Rick Rich",                        "deposit": 100,                        "children": [                            { "deposit": 100 },                            { "deposit": 100 },                            { "deposit": 100 }                        ]                    },                    { "deposit": 100, "children": [] },                    { "deposit": 100, "children": [] }                ]            },            { "deposit": 100, "children": [] },            { "deposit": 100, "children": [] }        ]    },    { "deposit": 100, "children": [] },    { "deposit": 0, "children": [] }]

期望的输出结果是一个数组,例如 [300, 300, 300, 300],表示第一层总和、第二层总和、第三层总和等。

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

错误的尝试及原因分析

初学者在处理此类问题时,可能会尝试使用简单的迭代或递归,但容易陷入将所有存款扁平化收集的误区。例如,以下代码会收集所有节点的存款,而不是按层级汇总:

// 假设 Children 类型已定义// const [userDeposit, setUserDeposit] = useState([]); // 在React等框架中可能这样使用const iterateOfChildrenDepositWrong = (    children: any[], // 简化类型为any[]    result: number[] = [],): void => {    children.forEach((node: any) => {        result.push(node.deposit); // 这里直接将存款添加到结果数组        if (node.children && node.children.length > 0) {            iterateOfChildrenDepositWrong(node.children, result);        }    });    // setUserDeposit(result); // 在实际应用中可能会更新状态};// 调用示例// let allDeposits: number[] = [];// iterateOfChildrenDepositWrong(initialData, allDeposits);// console.log(allDeposits); // 输出将是所有存款的扁平列表,如 [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0]

上述代码的问题在于,它在遍历每个节点时,直接将其deposit值添加到同一个result数组中。当递归调用处理子节点时,子节点的存款也会被添加到这个result数组中,导致最终得到的是所有层级存款的混合列表,无法区分每个层级的总和。要实现按层级汇总,我们需要在每次处理一个层级时,计算该层级的总和,并将其作为一个独立的元素添加到结果数组中。

正确的层级汇总方法:递归与广度优先思想

为了按层级汇总存款,我们需要一种方法来在处理完一个层级的所有节点后,再进入下一个层级。这本质上是一种广度优先遍历(BFS)的思想,但通过递归也可以优雅地实现。核心思路是:

初始化结果数组:用于存储每个层级的存款总和。处理当前层级:遍历当前层级的所有节点,计算它们的存款总和。收集下一层级节点:在处理当前层级的同时,将所有当前节点的子节点收集起来,形成“下一层级”的节点列表。递归调用:如果下一层级存在节点,则以这些节点作为新的“当前层级”进行递归调用。

示例代码

以下是实现按层级汇总存款的JavaScript函数:

// 简化后的示例数据,只包含deposit和children字段let hierarchicalData = [    {        "deposit": 100,        "children": [            {                "deposit": 100,                "children": [                    {                        "deposit": 100,                        "children": [                            { "deposit": 100 },                            { "deposit": 100 },                            { "deposit": 100 }                        ]                    },                    { "deposit": 100, "children": [] },                    { "deposit": 100, "children": [] }                ]            },            { "deposit": 100, "children": [] },            { "deposit": 100, "children": [] }        ]    },    { "deposit": 100, "children": [] },    { "deposit": 0, "children": [] }];let levelWiseDeposits: number[] = []; // 用于存储最终结果的数组/** * 递归函数,用于按层级汇总存款 * @param children 当前层级的节点数组 * @param result 存储每个层级总和的数组(通过引用传递) */function iterateOfChildrenDeposit(children: any[], result: number[]): void {    let currentLevelSum = 0; // 当前层级的存款总和    let nextLevelChildren: any[] = []; // 存储下一层级的所有子节点    // 遍历当前层级的所有节点    children.forEach((node: any) => {        currentLevelSum += node.deposit; // 累加当前节点的存款        // 如果当前节点有子节点,则将其添加到下一层级列表中        if (node.children && node.children.length > 0) {            nextLevelChildren = nextLevelChildren.concat(node.children);        }    });    // 将当前层级的总和添加到结果数组中    result.push(currentLevelSum);    // 如果下一层级存在节点,则递归调用自身处理下一层级    if (nextLevelChildren.length > 0) {        iterateOfChildrenDeposit(nextLevelChildren, result);    }    // 递归终止条件:当nextLevelChildren为空时,表示没有更多层级}// 调用函数开始计算iterateOfChildrenDeposit(hierarchicalData, levelWiseDeposits);console.log('按层级汇总的存款:', levelWiseDeposits);// 预期输出: [300, 300, 300, 300]

代码解析

levelWiseDeposits: number[] = []: 这是一个在函数外部声明的数组,用于存储最终的按层级汇总结果。它作为参数传递给递归函数,并通过引用在每次递归调用中被修改。iterateOfChildrenDeposit(children: any[], result: number[]): void:children: 代表当前正在处理的层级的所有节点。result: 引用外部的levelWiseDeposits数组,用于累积每个层级的总和。currentLevelSum = 0;: 在每次函数调用(即处理一个新的层级)开始时,都会初始化一个变量来累加当前层级的存款。nextLevelChildren: any[] = [];: 同样,每次处理新层级时,都会初始化一个空数组来收集当前层级所有节点的子节点,这些子节点将构成下一个层级。children.forEach((node: any) => { … });: 遍历children数组(当前层级的所有节点)。currentLevelSum += node.deposit;: 将当前节点的存款加到currentLevelSum中。if (node.children && node.children.length > 0) { nextLevelChildren = nextLevelChildren.concat(node.children); }: 检查当前节点是否有子节点。如果有,则将这些子节点添加到nextLevelChildren数组中。concat方法用于将多个子节点数组合并成一个。result.push(currentLevelSum);: 在遍历完当前层级的所有节点并计算出currentLevelSum后,将其添加到result数组中。这确保了每个元素都是一个层级的总和。if (nextLevelChildren.length > 0) { iterateOfChildrenDeposit(nextLevelChildren, result); }: 这是一个关键的递归步骤。如果nextLevelChildren数组不为空(即存在下一层级),则以nextLevelChildren作为新的children参数,递归调用iterateOfChildrenDeposit函数,继续处理下一层级。递归终止条件: 当nextLevelChildren.length为0时,表示当前层级的所有节点都没有子节点,即没有下一层级了,递归调用停止。

注意事项与扩展

数据深度限制: 原始问题中提到最大5层,但此递归方法可以处理任意深度的层级结构,只要不超出JavaScript的递归深度限制(通常很高,对于5层来说完全不是问题)。空节点或无存款: 如果某个节点没有deposit字段或其值为null/undefined,在实际应用中需要进行额外的检查或默认值处理,以避免NaN。本示例假设deposit始终为有效数字。性能考量: 对于非常庞大和深层的数据结构,递归可能会导致栈溢出。在这种极端情况下,可以考虑使用基于队列的迭代式广度优先搜索(BFS)算法来避免深层递归。然而,对于大多数实际应用(如5层),递归是简洁高效的选择。通用性: 此模式不仅适用于计算存款总额,还可以轻松修改以计算其他数值属性的总和、计数节点数量、收集特定属性等,只需调整currentLevelSum的计算逻辑和result数组的存储内容即可。框架集成: 在React等前端框架中,可以将levelWiseDeposits作为组件的状态(useState),并在数据更新时调用此函数来重新计算。

总结

通过采用递归与广度优先遍历思想相结合的方法,我们能够有效地按层级汇总嵌套数据结构中的数值。这种方法清晰地分离了不同层级的处理逻辑,确保了结果的准确性。理解并掌握这种处理层级数据的模式,对于开发涉及树形结构的应用至关重要。

以上就是深度解析:JavaScript中如何按层级汇总嵌套数据结构中的金额的详细内容,更多请关注创想鸟其它相关文章!

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

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Laravel 路由参数缺失问题排查与解决:以会话功能为例
上一篇 2025年12月20日 19:33:05
动态链接文件下载:解决跨域与标签download属性失效问题
下一篇 2025年12月20日 19:33:20

相关推荐

  • 使用 DynamoDBMapper 进行条件更新操作

    使用 DynamoDBMapper 进行条件更新操作使用 DynamoDBMapper 进行条件更新操作使用 DynamoDBMapper 进行条件更新操作使用 DynamoDBMapper 进行条件更新操作

    本文将介绍如何利用 DynamoDBMapper 在 Java 中执行基于当前值的条件更新操作,特别是使用 “ADD” 操作来递减账户余额。虽然 DynamoDBMapper 默认不支持直接使用更新表达式,但通过配置 SaveBehavior,可以实现类似的效果。 Dynam…

    2026年9月25日 • 用户投稿
    000
  • 解决JavaFX应用导出为可运行JAR后FXMLLoader资源加载失败的问题

    解决JavaFX应用导出为可运行JAR后FXMLLoader资源加载失败的问题解决JavaFX应用导出为可运行JAR后FXMLLoader资源加载失败的问题解决JavaFX应用导出为可运行JAR后FXMLLoader资源加载失败的问题解决JavaFX应用导出为可运行JAR后FXMLLoader资源加载失败的问题

    本文旨在解决JavaFX应用在Eclipse中正常运行,但导出为可运行JAR包后,因FXMLLoader无法找到FXML资源文件而抛出IllegalStateException: Location is not set异常的问题。核心解决方案是调整FXMLLoader.setLocation()方法…

    2026年9月25日 • 用户投稿
    100
  • Java多态中成员变量是否具有动态绑定特性

    成员变量不具有动态绑定特性,其访问基于引用变量的声明类型而非实际对象类型。例如,当父类和子类存在同名成员变量时,通过父类引用访问该变量将获取父类中的值,即使实际对象是子类实例。这体现了静态绑定,即在编译期确定访问的变量。相比之下,实例方法支持动态绑定(后期绑定),在运行时根据对象的实际类型决定调用哪…

    2026年9月25日
    100
  • Java 中处理货币数据的正确方式

    Java 中处理货币数据的正确方式Java 中处理货币数据的正确方式Java 中处理货币数据的正确方式Java 中处理货币数据的正确方式

    在 Java 应用程序中,尤其是在处理财务数据时,选择正确的数据类型至关重要。货币数据通常以特定的格式呈现,例如包含货币符号(如美元符号 $)和千位分隔符(如逗号 ,)。直接将这些数据映射到 DTO 类时,我们需要仔细考虑数据类型的选择,以避免潜在的精度损失和计算错误。 货币数据类型选择考量 常见的…

    2026年9月25日 • 用户投稿
    000
  • Java 中处理货币数据的最佳实践

    Java 中处理货币数据的最佳实践Java 中处理货币数据的最佳实践Java 中处理货币数据的最佳实践Java 中处理货币数据的最佳实践

    本文旨在探讨在 Java 中处理货币数据的最佳实践。面对 JSON 数据中包含的货币值(例如 “$234,205,860″),直接使用 String 存储是一种选择,但可能并非最优。本文将深入分析各种数据类型在处理货币时的优劣,并推荐使用 BigDecimal 进行精确计算,…

    2026年9月25日 • 用户投稿
    000
  • Java向上转型中可变参数方法调用的行为解析:重载与编译时绑定的深层机制

    Java向上转型中可变参数方法调用的行为解析:重载与编译时绑定的深层机制Java向上转型中可变参数方法调用的行为解析:重载与编译时绑定的深层机制Java向上转型中可变参数方法调用的行为解析:重载与编译时绑定的深层机制Java向上转型中可变参数方法调用的行为解析:重载与编译时绑定的深层机制

    本文深入探讨Java中向上转型、方法重载与可变参数(varargs)的交互机制。通过具体代码示例,详细解释了在向上转型场景下,为何编译器会基于引用变量的编译时类型来解析方法调用,即使子类存在看似更匹配的重载方法。核心在于方法重载是编译时决策,而可变参数在重载解析中具有较低的优先级。理解这些机制对于编…

    2026年9月25日 • 用户投稿
    000
  • Groovy编程:在HTTP请求头中传递授权令牌的实践指南

    Groovy编程:在HTTP请求头中传递授权令牌的实践指南Groovy编程:在HTTP请求头中传递授权令牌的实践指南Groovy编程:在HTTP请求头中传递授权令牌的实践指南Groovy编程:在HTTP请求头中传递授权令牌的实践指南

    本教程详细介绍了如何在Groovy中通过HTTP请求头发送授权令牌,以实现对RESTful API的安全访问。针对用户在Groovy中模拟curl -H ‘Authorization: token …’命令时遇到的常见问题,本文提供了基于java.net.URL和…

    2026年9月25日 • 用户投稿
    900
  • 快速搭建一个管理App数据和用户的界面

    快速搭建一个管理App数据和用户的界面快速搭建一个管理App数据和用户的界面快速搭建一个管理App数据和用户的界面快速搭建一个管理App数据和用户的界面

    在电商、教育、企业服务等关键领域,app的数据管理效率与系统用户体验已成为决定产品市场竞争力的核心因素。本文将为开发者提供一套从需求分析到技术落地的完整路径,助你快速构建一个高效且易用的管理类app界面。 一、厘清需求:聚焦数据与用户场景的深度融合 构建管理型App的第一步是精准把握业务本质。必须深…

    2026年9月25日 • 用户投稿
    700
  • Spring Boot 应用:分离 REST API 和 Web 应用的最佳实践

    Spring Boot 应用:分离 REST API 和 Web 应用的最佳实践Spring Boot 应用:分离 REST API 和 Web 应用的最佳实践Spring Boot 应用:分离 REST API 和 Web 应用的最佳实践Spring Boot 应用:分离 REST API 和 Web 应用的最佳实践

    本文旨在探讨在 Spring Boot 项目中,如何有效地分离 REST API 和 Web 应用程序。针对小型项目,建议保持简单,将代码放在同一模块的不同包中。对于大型项目,则需要考虑可伸缩性、团队协作和性能需求,将前后端分离成两个独立的 Spring Boot 应用。文章将深入分析不同场景下的架…

    2026年9月25日 • 用户投稿
    300
  • uc浏览器如何阻止网页自动播放视频_UC浏览器禁止视频自动播放设置

    uc浏览器如何阻止网页自动播放视频_UC浏览器禁止视频自动播放设置uc浏览器如何阻止网页自动播放视频_UC浏览器禁止视频自动播放设置uc浏览器如何阻止网页自动播放视频_UC浏览器禁止视频自动播放设置uc浏览器如何阻止网页自动播放视频_UC浏览器禁止视频自动播放设置

    1、可通过UC浏览器设置关闭自动播放:进入设置→网站管理→媒体自动播放,选择关闭;2、启用省流加速模式减少视频自动加载;3、使用隐私模式浏览限制脚本执行,阻止视频自动播放。 如果您在使用UC浏览器浏览网页时,遇到视频自动播放影响阅读体验的情况,可以通过调整浏览器设置来禁止此行为。自动播放功能通常由浏…

    2026年9月25日 • 用户投稿
    600
  • 通过索引访问 LinkedHashMap 的值

    通过索引访问 LinkedHashMap 的值通过索引访问 LinkedHashMap 的值通过索引访问 LinkedHashMap 的值通过索引访问 LinkedHashMap 的值

    通过索引访问 LinkedHashMap 的值 本文将探讨如何比较两个 LinkedHashMap 中具有相同键的值,并提供一种有效的解决方案。LinkedHashMap 是一种可以保持插入顺序的 Map 实现,但它并不支持像 List 那样通过索引直接访问元素。因此,当我们需要比较两个 Linke…

    2026年9月25日 • 用户投稿
    1200
  • 通过索引获取 LinkedHashMap 的值?解决方案与最佳实践

    通过索引获取 LinkedHashMap 的值?解决方案与最佳实践通过索引获取 LinkedHashMap 的值?解决方案与最佳实践通过索引获取 LinkedHashMap 的值?解决方案与最佳实践通过索引获取 LinkedHashMap 的值?解决方案与最佳实践

    本文旨在解决如何比较两个 LinkedHashMap 中具有相同键(chargeTypeName)的值的问题。由于 LinkedHashMap 本身不支持通过索引直接访问,文章将探讨如何利用流(Stream)和分组(Grouping)等技术,有效地找出两个 LinkedHashMap 中键相同的值对…

    2026年9月25日 • 用户投稿
    100
  • 使用 Jackson 进行复杂类的自定义反序列化

    使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化

    本文介绍了如何使用 Jackson 库对包含复杂嵌套类的 JSON 字符串进行自定义反序列化。通过 ObjectMapper 的 readValue 方法可以实现简单场景下的自动反序列化。针对需要定制化处理的场景,可以结合 ObjectMapper 和自定义反序列化器来实现更灵活的反序列化逻辑,并提…

    2026年9月25日 • 用户投稿
    1000
  • 使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示

    使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示

    本文旨在帮助开发者理解并解决在使用 JSON Schema 验证 JSON 数据时,由于 oneOf 约束可能导致的“无效枚举值”的错误提示问题。我们将通过示例代码,详细解释错误产生的原因,并提供相应的解决方案,以确保 JSON Schema 验证的准确性和可读性。 在使用 JSON Schema …

    2026年9月25日 • 用户投稿
    500
  • p5.js WebGL性能优化:首帧渲染耗时长的原因与对策

    p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策

    在使用p5.js的WEBGL渲染模式时,首次调用image()函数渲染图片或p5.Graphics对象通常会比后续调用耗时显著增加。这主要是因为第一次渲染时,p5.js需要将图像数据从CPU内存上传到GPU的纹理内存中,涉及内存分配和数据复制,这是一个相对耗时的过程。后续调用由于纹理已被缓存,可以直…

    2026年9月25日 • 用户投稿
    800
  • sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置

    sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置

    首先安装SublimeLinter和SublimeLinter-eslint插件,确保系统或项目中已安装ESLint;通过npx eslint –init生成配置文件;插件会自动调用项目内的eslint,若未识别可手动设置executable路径;保存JavaScript文件时即可实时显…

    2026年9月25日 • 用户投稿
    100
  • Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    本文将详细介绍如何使用 Java 8 的 Stream API 将一个嵌套的 Map 结构进行扁平化处理,并从中提取所需的数据。 具体来说,我们将把 Map<Integer, Map<String, List>> 转换为 Map,其中新 Map 的键是原内部 Map 的键,值…

    2026年9月25日 • 用户投稿
    1300
  • 修改 Android KeyStore 中 KeyPair 的用途

    修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途

    本文档介绍了如何在 Android KeyStore 中修改现有 KeyPair 的用途,使其支持密钥协商 (Key Agreement) 操作。通过示例代码展示了如何利用 KeyStore.setEntry 方法在 Android 13 (API 33) 及以上版本中导入 KeyPair 并设置所…

    2026年9月25日 • 用户投稿
    700
  • 并发处理共享列表并收集结果的方案

    并发处理共享列表并收集结果的方案并发处理共享列表并收集结果的方案并发处理共享列表并收集结果的方案并发处理共享列表并收集结果的方案

    本文旨在介绍如何利用 Java 并行流高效地处理大型列表,尤其是在每个元素的处理过程耗时较长的情况下。并行流能够将列表分割成多个子任务,并在多个线程上并发执行,从而显著提升处理速度。但同时,并发编程也带来了共享资源同步的问题,需要谨慎处理。 使用并行流并发处理列表 假设我们有一个 Foo 类,其 p…

    2026年9月25日 • 用户投稿
    100
  • 高效并发处理共享列表与结果收集的Java教程

    高效并发处理共享列表与结果收集的Java教程高效并发处理共享列表与结果收集的Java教程高效并发处理共享列表与结果收集的Java教程高效并发处理共享列表与结果收集的Java教程

    本文介绍了如何利用Java并发特性,特别是并行流(Parallel Streams),来高效处理共享列表,并将处理结果进行收集。针对耗时操作,通过将列表分割成子列表,并利用并行流并发执行,可以显著提高处理效率。同时,强调了在并发环境下对共享资源进行同步的重要性,并提供了收集处理结果的示例代码。 在处…

    2026年9月25日 • 用户投稿
    100

发表回复

登录后才能评论
关注微信