前端对集合分组求和
项目需求:现在有一个需求,根据集合中某一对象的键值,对集合中相同键值的对象重新求和。如
[{name: 'Jack',country:1,score:68},
{name: 'Peter',country:1,score:75},
{name: 'Jason',country:2,score:55},
{name: 'David',country:2,score:89},
{name: 'Sean',country:3,score:85},
{name: 'Andy',country:3,score:99}]
这样一组集合,前端直接渲染成表格是这样的。
name | country | score |
---|---|---|
Jack | 1 | 68 |
Peter | 1 | 75 |
Jason | 2 | 55 |
David | 2 | 89 |
Sean | 3 | 85 |
Andy | 3 | 99 |
至于如何渲染成这样的,可以使用for循环,使用键值对,js动态插入到dom节点中。
//渲染表格
var str = '';
for(var i=0;i<obj.length;i++){
str += '<tr>'
str += '<td>'+obj[i].name+'<td>'
str += '<td>'+obj[i].grage+'<td>'
str += '<td>'+obj[i].score+'<td>'
str += '</tr>'
}
但是现在我想要的是各个国家的总分数,应该是这样的表格
name | grage | score |
---|---|---|
Jack | 1 | 68 |
Peter | 1 | 75 |
国家得分小计 | 1 | 143 |
Jason | 2 | 55 |
David | 2 | 89 |
国家得分小计 | 2 | 144 |
Sean | 3 | 85 |
Andy | 3 | 99 |
国家得分小计 | 3 | 184 |
这个应该是数据库中的一个分组求和的算法,那么如果我们的后台不够强大,只能够给我提供最简单的数据查询支持,而不提供分组求和的功能,那么纯粹前端如何实现呢?
function groupBy(obj) {
var newObj1 = new Array();
var newObj2 = JSON.parse(JSON.stringify(obj));
var index1 = 0;
var sum = -1;
for(var i = 0; i < newObj2.length; i++) {
newObj2[i].name = '国家积分小计'
if(i == 0) {
newObj1[i] = newObj2[i];
} else {
var res = newObj1.filter((item, index, arr) => {
if(newObj2[i].country == item.country) {
index1 = index;
}
return newObj2[i].country == item.country;
})
if(res.length == 0) {
newObj1.push(newObj2[i])
sum++;
obj.splice(i + sum, 0, newObj1[index1])
} else {
newObj1[index1].score += newObj2[i].score;
}
}
}
obj[obj.length] = newObj1[index1];
return obj;
}
这是重算完返回的数据:
[{"name":"Jack","country":1,"score":68},
{"name":"Peter","country":1,"score":75},
{"name":"国家积分小计","country":1,"score":143},
{"name":"Jason","country":2,"score":55},
{"name":"David","country":2,"score":89},
{"name":"国家积分小计","country":2,"score":144},
{"name":"Sean","country":3,"score":85},
{"name":"Andy","country":3,"score":99},
{"name":"国家积分小计","country":3,"score":184
可以看到集合已经变成了我们想要的模样,现在只需要重新写进去便可以了。就能展示出来我们想要的结果。还可以在渲染界面的时候,根据当前name,是否为‘国家积分小计’,对单行数据重新调整样式,便可以了。
那么回头可以来看一下,究竟是如何实现的呢。首先逐条解析代码:
var newObj1 = new Array();
var newObj2 = JSON.parse(JSON.stringify(obj));
var index1 = 0;
var sum = -1;
这是生命的对象和变量,可以发现newObj2 这个对象,是由传入的obj对象复制而来,这是深拷贝的结果,关于深拷贝和浅拷贝的问题,可以自己查询一下,现在这是一个新的对象,并且和obj是一样的。
for(var i = 0; i < newObj2.length; i++) {
newObj2[i].name = '国家积分小计'
if(i == 0) {
newObj1[i] = newObj2[i];
} else {
var res = newObj1.filter((item, index, arr) => {
if(newObj2[i].country == item.country) {
index1 = index;
}
return newObj2[i].country == item.country;
})
if(res.length == 0) {
newObj1.push(newObj2[i])
sum++;
obj.splice(i + sum, 0, newObj1[index1])
} else {
newObj1[index1].score += newObj2[i].score;
}
}
}
在这段循环中,可以看出来是newObj2集合的循环,首先将集合中的每个对象的name设置为国家积分小计,首先将newObj2的第一个对象赋给newObj1的第一个对象,这是方便使用js中的filter方法,关于这种方法,不知道的可以查询。在第一行以后,开始判断newObj2当前对象是否在newObj1之中,如果在,则使用index1 去记录下来newObj1的下标,即要知道,对应于newObj1的哪一个对象。如果res.length的结果为0,则说明newObj1不存在与当前newObj2国家相同的对象,便将当前newObj2的对象添加到newObj1中。并且在原集合obj中,添加这一对象。如果res.length不为0,则说明当前国家是一样的,便在newObj2的得分中加上当前newObj1的得分。接下来依次循环。最后将newObj2的最后一个对象添加到newObj1中。
总结一下,即首先复制一个当前集合用于编辑,再创建一个空集合用于储存分组求和的集合。最后将分组求和的集合,按国家名称依次写入到原集合中。