在数组中对对象进行分组的最有效方法是什么?
例如,给定此对象数组:
[
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]
我在表格中显示这些信息 . 我想用不同的方法分组,但我想总结这些值 .
我正在使用Underscore.js作为其groupby函数,这很有帮助,但并没有完成整个技巧,因为我不希望它们“拆分”但是“合并”,更像是SQL group by
方法 .
我正在寻找的是能够总计特定值(如果要求) .
所以,如果我按照 Phase
进行分组,我希望收到:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
如果我做了很多 Phase
/ Step
,我会收到:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
是否有一个有用的脚本,或者我应该坚持使用Underscore.js,然后循环结果对象自己做总计?
30 回答
我会检查lodash groupBy它似乎完全符合您的要求 . 它也非常轻巧,非常简单 .
小提琴示例:https://jsfiddle.net/r7szvt5k/
如果你的数组名是
arr
,那么带有lodash的groupBy就是:此解决方案采用任意函数(不是键),因此它比上述解决方案更灵活,并且允许arrow functions,类似于 LINQ 中使用的lambda expressions:
注意:是否要扩展
Array
的原型取决于您 .Example supported in most browsers:
Example using arrow functions (ES6):
以上两个例子都返回:
根据以前的答案
如果您的环境支持,使用对象扩展语法来查看它会更好一些 .
这里,我们的reducer采用部分形成的返回值(从空对象开始),并返回一个由前一个返回值的展开成员组成的对象,以及一个新成员,其成员的密钥是根据当前迭代的值来计算的 .
prop
,其值是该道具的所有值以及当前值的列表 .这是一个ES6版本,不会在null成员上中断
如果你想避免使用外部库,你可以简洁地实现
groupBy()
的vanilla版本,如下所示:我想建议我的方法 . 首先,单独分组和聚合 . 让我们宣布典型的“分组依据”功能 . 它需要另一个函数来为每个要分组的数组元素生成“哈希”字符串 .
分组完成后,您可以根据需要汇总数据
共同的是它有效 . 我已经在chrome控制台中测试了这段代码 . 随时改进并发现错误;)
Ceasar的答案很好,但仅适用于数组内部元素的内部属性(长度为字符串) .
这个实现更像是:this link
希望这可以帮助...
使用linq.js可能更容易做到这一点,_25313旨在成为JavaScript中的LINQ的真正实现(DEMO):
结果:
或者,更简单地使用基于字符串的选择器(DEMO):
尽管linq答案很有意思,但它的重量也很重 . 我的方法有些不同:
你可以看到in action on JSBin .
我没有在Underscore中看到任何
has
所做的事情,尽管我可能会错过它 . 它与_.contains
非常相似,但使用_.isEqual
而不是===
进行比较 . 除此之外,其余部分是特定于问题的,尽管试图是通用的 .现在
DataGrouper.sum(data, ["Phase"])
返回并且
DataGrouper.sum(data, ["Phase", "Step"])
返回但
sum
只是这里的一个潜在功能 . 您可以随意注册其他人:现在
DataGrouper.max(data, ["Phase", "Step"])
将返回或者如果你注册了这个:
然后调用
DataGrouper.tasks(data, ["Phase", "Step"])
会得到你DataGrouper
本身就是一个功能 . 您可以使用您的数据和要分组的属性列表来调用它 . 它返回一个数组,其元素是具有两个属性的对象:key
是分组属性的集合,vals
是一个对象数组,包含不在键中的其余属性 . 例如,DataGrouper(data, ["Phase", "Step"])
将产生:DataGrouper.register
接受一个函数并创建一个新函数,该函数接受初始数据和要分组的属性 . 然后,这个新函数采用上面的输出格式,依次对每个函数运行函数,返回一个新数组 . 生成的函数根据您提供的名称存储为DataGrouper
的属性,如果您只想要本地引用,也会返回该函数 .那是很多解释 . 我希望代码相当简单!
来自:http://underscorejs.org/#groupBy
与ES6:
没有突变:
这个输出数组 .
虽然问题有一些答案,答案看起来有点复杂,但我建议使用vanilla Javascript进行分组 .
此解决方案具有一个函数,该函数使用数据
array
和一个返回col
的属性名称以及用于计算值value
的属性名称 .该函数依赖于一个对象,该对象充当了一个哈希表结果 .
你可以通过以下方式完成 . 我刚刚形成了新的数组,并从 groupBy 函数返回 . 通过 .map 函数循环计算的计数
MDN在其
Array.reduce()
文档中有this example .您可以使用Alasql JavaScript库来执行此操作:
试试这个例子at jsFiddle .
BTW: 在大型阵列上(100000条记录及更多)Alasql更快到了Linq . 见test at jsPref .
评论:
这里我将Value放在方括号中,因为VALUE是SQL中的关键字
我必须使用CAST()函数将字符串值转换为数字类型 .
让我们生成一个通用的
Array.prototype.groupBy()
工具 . 只是为了变化,让's use ES6 fanciness the spread operator for some Haskellesque pattern matching on a recursive approach. Also let'使我们的Array.prototype.groupBy()
接受一个回调,它将项目(e
)的索引(i
)和应用的数组(a
)作为参数 .检查答案 - 不是分组解决,但是直接答案 .
具有计算的键名称的某些字段的REAL GROUP BY用于对象数组 .
使用
estKey
进行游戏 - 您可以按多个字段进行分组您还可以递归地对数据进行分组 . 例如,最初按
Phase
分组,然后按Step
字段分组 .自己验证,运行它 . Этотосамоеоно,чтолюдиназываютгруппировкой?
祝你成功 .
Даздравствуютвысокиепоказателимастерствапрограммистоввоимяпроцветаниявсегочеловечества! Ура,товарищи!
我从underscore.js fiddler借用了这个方法
只是为了添加到Scott Sauyet的answer,有些人在评论中询问如何使用他的函数来组合value1,value2等,而不是仅仅分组一个值 .
只需要编辑他的求和函数:
离开主要一个(DataGrouper)不变:
使用ES6 Map对象:
用法示例:
jsfiddle:https://jsfiddle.net/buko8r5d/
关于 Map :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
我已经扩展了接受的答案,包括多个属性的分组,添加thenby并使其纯粹功能,没有变异 . 观看https://stackblitz.com/edit/typescript-ezydzv的演示
从@mortb,@ jmarceli回答和this post,
我利用
JSON.stringify()
作为 PRIMITIVE VALUE group by的多个列的标识 .没有第三方
与Lodash第三方合作
随着 sort feature
样品:
您可以从
array.reduce()
构建ES6Map
.与其他解决方案相比,这有一些优势:
它不需要任何库(不像例如
_.groupBy()
)你得到一个JavaScript
Map
而不是一个对象(例如由_.groupBy()
返回) . 这有lots of benefits,包括:它会记住首次添加项目的顺序,
键可以是任何类型而不仅仅是字符串 .
Map
是一个数组数组更有用的结果 . 但是如果你想要一个数组数组,那么你可以调用Array.from(groupedMap.entries())
(对于[key, group array]
对的数组)或Array.from(groupedMap.values())
(对于一个简单的数组数组) .这很灵活;通常,无论您计划在下一次使用此 Map 做什么,都可以直接作为缩减的一部分来完成 .
作为最后一点的一个例子,假设我有一个对象数组,我想通过id进行(浅)合并,如下所示:
为此,我通常首先按id分组,然后合并每个结果数组 . 相反,您可以直接在
reduce()
中进行合并: