在数据处理和前端开发中,我们经常需要对数组进行分组操作。传统的分组通常基于某个固定属性值,将所有具有相同属性值的元素归为一组。然而,有时我们会遇到一种特殊需求:需要根据元素在数组中的顺序,以及相邻元素之间某个特定属性值的变化来动态地进行分组。例如,给定一个对象数组,我们希望将连续的、某个属性值相同的对象归为一个子数组,一旦该属性值发生变化,就开启一个新的子数组。
这种分组逻辑的核心在于“变化检测”。当我们遍历数组时,需要实时比较当前元素的某个属性值与前一个元素的该属性值。
对于数组的第一个元素,由于没有前一个元素可以比较,它自然会开启第一个分组。
Array.prototype.reduce() 方法是实现这种动态分组的理想工具。它允许我们遍历数组,并累积一个结果,这个结果可以是任何类型,包括一个包含多个子数组的数组。
让我们通过一个具体的示例来理解其实现。假设我们有以下数据结构:
立即学习“Java免费学习笔记(深入)”;
const data = [ {name: 'A', number: 1, order: 1}, {name: 'B', number: 1, order: 2}, {name: 'C', number: 1, order: 3}, {name: 'D', number: 2, order: 4}, {name: 'E', number: 2, order: 5}, {name: 'F', number: 1, order: 6} ];
我们的目标是将其转换为:
[ [ {name: 'A', number: 1, order: 1}, {name: 'B', number: 1, order: 2}, {name: 'C', number: 1, order: 3}, ], [ {name: 'D', number: 2, order: 4}, {name: 'E', number: 2, order: 5}, ], [ {name: 'F', number: 1, order: 6} ] ]
可以看到,当number属性从1变为2(C到D),或从2变为1(E到F)时,都会开启新的子数组。
const data = [ {"name":"A","number":1,"order":1}, {"name":"B","number":1,"order":2}, {"name":"C","number":1,"order":3}, {"name":"D","number":2,"order":4}, {"name":"E","number":2,"order":5}, {"name":"F","number":1,"order":6} ]; let result = data.reduce((accumulator, currentObject, currentIndex, array) => { // 获取前一个对象的 'number' 属性值。 // 使用可选链操作符 '?' 处理第一个元素 (currentIndex === 0) 的情况, // 此时 array[currentIndex - 1] 为 undefined,其 .number 属性也将是 undefined。 const previousNumber = array[currentIndex - 1]?.number; // 检查当前对象的 'number' 属性是否与前一个对象的 'number' 属性不同。 // 对于第一个元素,previousNumber 是 undefined,而 currentObject.number 是实际值, // 所以它们必然不同,从而正确地开始第一个分组。 if (previousNumber !== currentObject.number) { // 如果不同,说明需要开始一个新的分组。 // 将包含当前对象的数组推入累加器中。 accumulator.push([currentObject]); } else { // 如果相同,说明当前对象属于上一个分组。 // 将当前对象推入累加器中最后一个子数组。 accumulator[accumulator.length - 1].push(currentObject); } // 返回累加器,供下一次迭代使用。 return accumulator; }, []); // 初始累加器为空数组,用于存放所有分组。 console.log(result);
data.reduce((accumulator, currentObject, currentIndex, array) => { ... }, []):
const previousNumber = array[currentIndex - 1]?.number;:
if (previousNumber !== currentObject.number):
accumulator.push([currentObject]);:
accumulator[accumulator.length - 1].push(currentObject);:
return accumulator;:
原始答案中使用了逗号表达式来简化代码,避免了 if/else 和 return 关键字。
const data = [{"name":"A","number":1,"order":1},{"name":"B","number":1,"order":2},{"name":"C","number":1,"order":3},{"name":"D","number":2,"order":4},{"name":"E","number":2,"order":5},{"name":"F","number":1,"order":6}]; let result = data.reduce((a,c,i,d)=> (d[i-1]?.number!==c.number ? a.push([c]) : a[a.length-1].push(c), a), []) console.log(result)
逗号表达式 (expr1, expr2, ..., exprN) 会从左到右依次执行每个表达式,并返回最后一个表达式的值。在这个例子中:
通过巧妙地利用 Array.prototype.reduce() 方法,结合对相邻元素属性的比较,我们可以实现一种强大且灵活的数组分组逻辑。这种方法特别适用于需要根据数据流中某个特定属性的变化来动态切分数组的场景,例如日志分析、时间序列数据处理或任何需要按序分组的业务需求。理解并掌握这种模式,将极大地增强您在 JavaScript 中处理复杂数据结构的能力。
以上就是JavaScript 数组高级分组:按相邻元素属性动态切片的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号