JavaScript/React中根据ID和引用ID实现复杂数组重排序教程

JavaScript/React中根据ID和引用ID实现复杂数组重排序教程

本文深入探讨如何在JavaScript/React环境中,根据数组元素的id和reference_id字段,实现对数组的复杂重排序。我们将介绍两种高效的解决方案,通过构建自定义排序键来将子元素归类到其父元素之后,从而实现清晰的层级结构展示,并提供示例代码和注意事项,帮助开发者应对此类数据组织挑战。

理解复杂数组重排序问题

前端开发中,我们经常会遇到需要根据特定逻辑对数据数组进行重排的需求。一个常见的场景是处理具有父子关系的数据结构,其中每个元素可能包含一个id和一个reference_id(或parent_id),reference_id指向其父元素的id。我们的目标是将所有子元素紧跟在它们的父元素之后,即使父元素本身没有reference_id(即它是顶级元素)。

考虑以下数据结构:

const initialArray = [  { id: 1, name: 'hello world', reference_id: null },  { id: 2, name: 'hello world', reference_id: null },  { id: 3, name: 'hello world', reference_id: 1 },  { id: 4, name: 'hello world', reference_id: null },  { id: 5, name: 'hello world', reference_id: 1 },  { id: 6, name: 'hello world', reference_id: 2 },];

我们期望的排序结果是:

[  { id: 1, name: 'hello world', reference_id: null },  { id: 3, name: 'hello world', reference_id: 1 },  { id: 5, name: 'hello world', reference_id: 1 },  { id: 2, name: 'hello world', reference_id: null },  { id: 6, name: 'hello world', reference_id: 2 },  { id: 4, name: 'hello world', reference_id: null },]

可以看到,id: 3和id: 5(它们的reference_id都是1)被排在了id: 1之后;id: 6(reference_id是2)被排在了id: 2之后。

直接使用 findIndex 和 splice 等方法来动态修改数组通常会导致复杂且难以维护的代码,尤其是在循环中修改数组长度和索引时,容易产生“意外结果”或逻辑错误。更推荐的做法是利用数组的 sort 方法,结合自定义的比较函数。

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

解决方案一:两步映射与排序

这种方法的核心思想是:首先为每个元素创建一个临时的排序键,然后根据这个键进行排序,最后移除这个临时键,还原为原始对象。

实现步骤

创建排序键: 遍历原始数组,为每个元素生成一个包含原始数据和排序键的新对象。排序键通常由reference_id和id组合而成。执行排序: 使用 Array.prototype.sort() 方法,根据新对象中的排序键进行比较。还原数据: 再次遍历排序后的数组,提取出原始数据对象。

示例代码

const arr = [  { id: 1, name: 'hello world', reference_id: null },  { id: 2, name: 'hello world', reference_id: null },  { id: 3, name: 'hello world', reference_id: 1 },  { id: 4, name: 'hello world', reference_id: null },  { id: 5, name: 'hello world', reference_id: 1 },  { id: 6, name: 'hello world', reference_id: 2 },];const sortedResult = arr  .map(item => ({    // 构建排序键:将 reference_id 视为父ID,如果为 null 则视为空字符串,    // 然后拼接当前 id。这样,父元素(reference_id为null)的键会以其id开头,    // 子元素(reference_id为父id)的键会以父id开头。    // 例如:    // id: 1, ref: null -> "1"    // id: 3, ref: 1    -> "13"    // id: 5, ref: 1    -> "15"    // id: 2, ref: null -> "2"    // id: 6, ref: 2    -> "26"    sortKey: `${item.reference_id ?? ''}${item.id}`,    originalElement: item,  }))  .sort((a, b) => a.sortKey.localeCompare(b.sortKey)) // 使用 localeCompare 进行字符串比较  .map(item => item.originalElement); // 还原原始对象console.log(sortedResult);

关键点解析

item.reference_id ?? ”: 使用空字符串来代替 null 的 reference_id,确保拼接时不会出现 null1 这样的字符串。localeCompare(b.sortKey): 这是一个字符串比较方法,它会根据当前语言环境的排序规则进行比较,对于数字字符串也能较好地处理。优点: 逻辑清晰,分步执行,易于理解。缺点: 增加了额外的内存开销,因为需要创建中间对象数组。对于非常大的数据集,这可能成为一个考虑因素。

解决方案二:单次排序与自定义比较函数

为了避免创建中间对象,我们可以直接在 sort 方法的比较函数中动态生成排序键。这种方法更高效,但比较函数的逻辑可能略显复杂。

实现步骤

定义排序键生成函数: 创建一个辅助函数,它接收一个元素,并返回一个用于比较的字符串。执行排序: 直接使用 Array.prototype.sort() 方法,并在比较函数中调用上述辅助函数来获取排序键。

示例代码

const arr = [  { id: 1, name: 'hello world', reference_id: null },  { id: 2, name: 'hello world', reference_id: null },  { id: 3, name: 'hello world', reference_id: 1 },  { id: 4, name: 'hello world', reference_id: null },  { id: 5, name: 'hello world', reference_id: 1 },  { id: 6, name: 'hello world', reference_id: 2 },];// 定义一个函数来生成元素的排序标准字符串const getSortCriterion = (item) => {  // 1. 处理 reference_id:如果为 null,则视为空字符串。  // 2. 使用 padStart(4, "0") 确保数字字符串长度一致,防止 "10"  "0001", id=10 -> "0010"  // 3. 使用 "|" 作为分隔符,确保 reference_id 和 id 的组合不会混淆。  //    例如:ref=1, id=2 -> "0001|0002"  //    ref=12, id=3 -> "0012|0003"  const refIdString = ("" + (item.reference_id ?? "")).padStart(4, "0");  const idString = ("" + item.id).padStart(4, "0");  return `${refIdString}|${idString}`;};// 对数组进行排序const sortedResult = [...arr].sort((a, b) =>   getSortCriterion(a).localeCompare(getSortCriterion(b)));console.log(sortedResult);console.log("计算出的排序标准(用于调试):");console.log(sortedResult.map(getSortCriterion));

关键点解析

getSortCriterion(item) 函数:这是核心。item.reference_id ?? “”: 同上,处理 null 值。.padStart(4, “0”): 这是解决数字字符串排序问题的关键。 如果不进行填充,”10″ 会在 localeCompare 中被认为小于 “2”(因为它们都是字符串,按字符逐个比较)。通过 padStart,1 变为 “0001”,10 变为 “0010”,2 变为 “0002”,这样 “0001” “|” 分隔符:确保 reference_id 和 id 的组合不会产生歧义。例如,如果 reference_id 是 1,id 是 23,组合是 123;如果 reference_id 是 12,id 是 3,组合也是 123。使用分隔符可以避免这种情况。[…arr].sort(…): sort() 方法会修改原数组。为了保持数组的不可变性(在React等框架中非常重要),我们通常会先创建一个浅拷贝 […arr],然后再对其进行排序。优点: 避免了创建中间数组,内存效率更高。比较函数可以处理更复杂的数字ID。缺点: 比较函数内部逻辑相对复杂,需要理解 padStart 的作用。

注意事项与最佳实践

不可变性: Array.prototype.sort() 方法会直接修改原数组。在 React 或其他需要保持数据不可变的场景中,务必先创建数组的浅拷贝,例如 […originalArray].sort(…),以避免副作用。ID 类型: 确保 id 和 reference_id 的数据类型一致,通常是数字或字符串。如果它们是混合类型,可能需要在比较函数中进行类型转换。padStart 的长度: 在解决方案二中,padStart(4, “0”) 假设 id 最大为四位数。如果 id 可能更大(例如五位数或更多),则需要相应地增加填充长度(例如 padStart(5, “0”))。性能考量: 对于非常大的数组,sort 方法的性能是 O(N log N)。在比较函数中执行复杂的字符串操作(如 padStart 和字符串拼接)会增加每次比较的开销。对于大多数前端应用,这通常不是问题,但如果遇到性能瓶颈,可能需要考虑其他数据结构或更优化的算法。多层级排序: 这两种方法都适用于简单的父子层级。如果需要处理多层嵌套的层级结构(例如祖父-父-子),可能需要更复杂的递归排序逻辑或将数据转换为树形结构后再进行扁平化处理。

总结

通过构建自定义的排序键,我们可以有效地解决根据 id 和 reference_id 对数组进行复杂重排序的问题。无论是采用两步映射和排序的清晰方法,还是单次排序与自定义比较函数的更高效方法,关键都在于设计一个能够准确反映期望顺序的比较逻辑。在实际开发中,根据项目需求和数据规模选择最合适的方案,并始终注意保持代码的不可变性和可维护性。

以上就是JavaScript/React中根据ID和引用ID实现复杂数组重排序教程的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
iframe 内容刷新不重置:实现持久化导航状态的教程
上一篇 2025年12月20日 11:59:38
AG Grid 固定列宽度限制与横向滚动实现教程
下一篇 2025年12月20日 11:59:46

相关推荐

  • Java中双精度浮点数的小数位控制技巧

    Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧

    本文深入探讨了在Java中有效控制double类型数值小数位数的方法。通过Math.round()函数结合乘除操作,可以实现数值本身的四舍五入并改变其精度;而String.format()则提供了灵活的字符串格式化功能,用于在不修改原始数值的情况下精确控制显示的小数位数。这两种方法分别适用于不同的业…

    2026年9月24日 用户投稿
    000
  • Java语法基础中static关键字可以修饰哪些内容

    static关键字用于定义类成员,包括静态变量(如计数器)、静态方法(如工具方法)、静态代码块(类加载时执行)和静态内部类(不依赖外部类实例),均属于类而非对象,通过类名访问,提升成员至类级别实现共享与提前使用。 static 关键字在 Java 中主要用于定义与类相关而非与对象实例相关的成员。它不…

    2026年9月24日
    100
  • mysql中*是什么意思 mysql星号通配符解析

    在 mysql 中,星号()最常用于 select 语句中代表所有列,但应谨慎使用。1)它方便查看所有数据,但可能返回不必要的数据,影响性能。2)使用可能降低代码可维护性,建议明确列出所需列。3)在like操作符中,不是通配符,需用regexp。4)在视图中使用可能导致定义失效。5)可结合limit…

    2026年9月24日
    000
  • Java语法基础中如何导入其他包中的类

    使用import关键字可导入其他包中的类,如import java.util.ArrayList;2. 通过import java.util.*可导入整个包;3. 不导入时可用全限定名访问类,但不推荐;4. 类名冲突时需使用全限定名区分,如java.sql.Date。 在Java中使用其他包中的类,…

    2026年9月24日
    1100
  • Java Map.entrySet遍历性能优化

    使用增强for循环遍历Map.entrySet()更高效,避免显式声明Iterator;提前缓存key和value减少重复调用;优先选用HashMap提升性能;大数据量可考虑parallelStream并行处理,但需权衡开销。 在Java中,Map.entrySet() 是遍历键值对最常用的方式之一…

    2026年9月24日
    100
  • Java泛型擦除机制对对象类型的影响

    泛型擦除使Java在编译后移除类型信息,导致运行时无法判断具体泛型类型,影响类型检查、反射获取及继承多态,需通过桥接方法等机制保证一致性。 Java的泛型擦除机制在编译期会移除泛型类型信息,导致运行时无法获取具体的泛型参数类型。这一机制直接影响了对象类型的判断、反射操作以及继承中的类型处理。 泛型擦…

    2026年9月24日
    300
  • PHP实时输出如何防止XSS攻击_PHP实时输出安全防范XSS攻击

    防止XSS攻击需坚持三重防护:首先对用户输入进行严格验证与白名单过滤,使用filter_var等函数校验数据格式;其次根据输出上下文进行恰当转义——HTML正文和属性用htmlspecialchars(),JavaScript变量用json_encode(),URL参数用urlencode();最后…

    2026年9月24日
    100
  • Java Optional.orElse与orElseGet区别

    orElse总是执行默认值计算,而orElseGet仅在Optional为空时调用Supplier获取,默认值构造 costly 时应优先使用orElseGet以避免性能浪费。 在 Java 8 引入的 Optional 类中,orElse 和 orElseGet 都用于在 Optional 值为空…

    2026年9月24日
    000
  • Java中接口常量和类常量的使用区别

    接口常量默认public static final,用于行为契约但易导致职责模糊;类常量可用不同访问修饰符,更适合封装和维护。现代Java推荐使用专用常量类、枚举、私有静态常量或配置文件管理常量,以提升代码清晰度与可维护性。 Java中接口常量和类常量,核心区别在于它们的定义位置和隐式属性。接口常量…

    2026年9月24日
    000
  • php数据如何实现文件断点续传_php数据大文件上传解决方案

    断点续传通过文件分片、唯一hash标识、服务端记录上传状态实现,前端切片上传并查询已传分片,PHP后端存储分片并在完成后合并,同时提供状态接口支持续传,需注意hash一致性与临时文件清理。 大文件上传在Web开发中是个常见需求,尤其是涉及视频、备份文件或资源包时。PHP本身对文件上传有一定限制,但通…

    2026年9月24日
    000
  • OOP中的继承机制在Java中是如何运作的

    Java通过extends实现继承,子类可复用父类属性和方法,提升代码可维护性;支持方法重写与super调用,遵循单继承与访问控制规则,构造函数需显式调用父类构造器。 Java中的继承机制通过extends关键字实现,允许一个类(子类)获取另一个类(父类)的属性和方法。这种机制支持代码重用,提升程序…

    2026年9月24日
    100
  • PHP 中如何将 JSON 数组值声明为变量

    本文介绍了如何在 PHP 中从数据库获取数据并将其编码为 JSON 格式,然后通过 AJAX 请求传递到另一个页面。重点讲解了如何在接收页面解析 JSON 数据,并将 JSON 数组中的特定值提取并赋值给变量,以便在后续的 PHP 函数中使用。 从数据库获取数据并编码为 JSON 首先,我们需要从数…

    2026年9月24日
    000
  • VSCode如何调试React前端应用 VSCode调试React组件的完整教程

    要调试react前端应用,首先需安装vscode的浏览器调试插件并配置launch.json文件,1. 安装“debugger for chrome”或对应浏览器的插件;2. 在项目根目录的.vscode文件夹中创建launch.json,配置type为chrome、request为launch、n…

    2026年9月24日
    100
  • 如何在Java中处理StackOverflowError

    StackOverflowError由无限递归或调用栈过深引发,属Error类型,需预防为主;2. 常见于递归无终止、循环调用或深度嵌套;3. 避免方法需设可达成的基准条件,如阶乘递归中n≤1时返回1。 Java中的StackOverflowError通常由无限递归或过深的调用栈引发,属于Error…

    2026年9月24日
    100
  • 深入理解 javac 命令中的 ‘当前目录’ 与类路径

    在使用 javac 命令进行 Java 编译时,’当前目录’ 指的是执行该命令时所在的目录,而非源代码文件或 Java 安装路径所在的目录。这对于默认类路径(.)的解析至关重要,影响编译器查找依赖类文件的位置。理解这一概念有助于避免编译错误,并正确配置类路径。 什么是“当前目…

    2026年9月24日
    100
  • Laravel 表单多动作处理:区分同一路由下的提交操作

    本教程将详细介绍如何在 laravel 应用中,通过一个 html 表单的多个提交按钮触发不同的后端操作,而无需为每个操作创建单独的表单或路由。核心方法是为提交按钮添加 `name` 和 `value` 属性,然后在控制器中根据这些属性的值来判断执行哪种业务逻辑,从而实现如更新用户角色和删除用户等多…

    2026年9月24日
    000
  • mysql中是什么意思 mysql语法符号含义解析

    mysql 中的符号和关键字是与数据库交互的基本工具,正确使用它们可以提高工作效率和查询准确性。1. 逗号(,)用于分隔列表中的元素,如列名和值。2. 点号(.)用于访问表中的列或调用函数。3. 星号(*)用于选择所有列,但应避免使用以提高查询性能。4. 百分号(%)用于 like 操作中的模式匹配…

    2026年9月24日
    100
  • DeepCode— 港大实验室推出的多Agent代码生成平台

    DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台DeepCode— 港大实验室推出的多Agent代码生成平台

    ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ MiniMax Agent MiniMax平台推出的Agent智能体助手 334 查看详情 DeepCode是什么 deepcode是由香港大学数据智能实验室研发的一款基于多智能体架构的智能代码…

    2026年9月24日 用户投稿
    200
  • Flyway多数据库与多环境配置:实现测试与生产环境的灵活迁移管理

    本文深入探讨了Flyway在多数据库和多环境场景下的灵活配置策略,旨在解决开发、开发、测试与生产环境数据库迁移的挑战。文章首先分析了测试环境数据库选择的推荐方案,包括使用与生产一致的数据库服务或Testcontainers。随后,详细阐述了Flyway如何通过分离配置文件、编程化配置以及利用占位符来…

    2026年9月24日
    100
  • 使用正则表达式从JSON数组中提取JSON对象

    本文旨在提供一种使用Java正则表达式从包含多个JSON对象的JSON数组中提取单个JSON对象的方法。我们将详细介绍如何构建合适的正则表达式,并提供示例代码演示如何在Java中使用该表达式来实现JSON对象的提取,并对提取后的字符串进行优化处理,移除不必要的空白字符。 从JSON数组中提取JSON…

    2026年9月24日
    000

发表回复

登录后才能评论
关注微信