我坚持如何在Javascript中实现这种相对简单的操作:
我有一个以这种方式定义的对象列表:
[
{id: 1, region: "America", country:"USA", values:[1,2,3,4] },
{id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
{id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
{id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
{id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
{id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
{id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
]
我试图计算值字段中包含的数字的平均值,按地区分组。 所以在上面的列表中,我将有两个元素,一个用于美国,一个用于欧洲,包含值的平均值:
[
{id: 1, region: "America", country:"USA", values:[1,2,3,4] },
{id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
{id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
{id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
{id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
{id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
{id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]},
{id: 8, region: "America", country:"avg", values:[2,3,4,5]},
{id: 9, region: "Europe", country:"avg", values:[2.8,4.6,2.8,6.4]}
]
有关如何做的任何想法? 请记住,聚合的元素数量可能在10~15左右,值字段的数量可以在150~200左右 value字段包含所有元素的相同数量的值。 某些值可能为null,因此在这种情况下我需要计算平均值,因为null不是0!
我可以做很多循环来扫描所有内容并进行计算,但我想知道是否有更容易,更快的东西,保持良好的表现。
说明/实施例:
美国的第一个平均值将按如下方式计算:
(sum of first value of 'values' for each country with region 'America')
-----------------------------------------------------------------------
(number of countries with region 'America')
伪代码:
America.avg.values[0] = (USA.values[0] + Canada.values[0]) / 2 /*(1+3)/2 = 2*/;
America.avg.values[1] = (USA.values[1] + Canada.values[1]) / 2 /*(2+4)/2 = 3*/;
...
答案 0 :(得分:3)
您可以使用一个对象来保持元素的总和和计数,并将值的每个循环的平均值分配给分组对象。
var data = [{ id: 1, region: "America", country: "USA", values: [1, 2, 3, 4] }, { id: 2, region: "America", country: "Canada", values: [3, 4, 5, 6] }, { id: 3, region: "Europe", country: "France", values: [1, 2, 3, 4] }, { id: 4, region: "Europe", country: "Italy", values: [1, 2, 3, 4] }, { id: 5, region: "Europe", country: "Spain", values: [5, 9, 1, 7] }, { id: 6, region: "Europe", country: "Germany", values: [1, 6, 2, 8] }, { id: 7, region: "Europe", country: "Ireland", values: [6, 4, 6, 9] }];
data.forEach(function (a, i, aa) {
if (!this[a.region]) {
this[a.region] = { sum: [], count: [], values: [] };
aa.push({ id: aa.length + 1, region: a.region, country: 'avg', values: this[a.region].values });
}
a.values.forEach(function (b, i) {
if (b !== null) {
this[a.region].sum[i] = (this[a.region].sum[i] || 0) + b;
this[a.region].count[i] = (this[a.region].count[i] || 0) + 1;
this[a.region].values[i] = this[a.region].sum[i] / this[a.region].count[i];
}
}, this);
}, Object.create(null));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
答案 1 :(得分:1)
减少原始数据,跟踪对象中的中间结果:
var data = [
{id: 1, region: "America", country:"USA", values:[1,2,null,4] },
{id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
{id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
{id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
{id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
{id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
{id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
];
var avg = data.reduce(function(result, current) {
if (result[current.region]) {
var obj = result[current.region];
obj.avg = current.values
// Map to a moving average:
// - the current avg at pos `i` represents `count` samples
.map(function(v, i) {
if (v === null) return obj.avg[i];
return (v + (obj.avg[i] * obj.count[i])) / (++obj.count[i]);
});
} else {
result[current.region] = {
count: current.values.map(function(v) {
return v !== null ? 1 : 0;
}),
avg: current.values.map(function(v) {
return v !== null ? v : 0;
})
};
}
return result;
}, {});
// Add to array (assume sorted by id)
var extendedData = data.concat(Object.keys(avg).map(function(k, i) {
return {
id: data[data.length - 1].id + 1 + i,
region: k,
country: "avg",
values: avg[k].avg
};
}));
console.log(extendedData);
答案 2 :(得分:0)
我只是遍历整个数组并计算平均值,然后在数组中添加avg
属性。
var data = [
{id: 1, region: "America", country:"USA", values:[1,2,3,4] },
{id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
{id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
{id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
{id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
{id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
{id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
];
data.forEach(function(element,index,array){
var sum = 0;
element.values.forEach(function(element,index,array){
sum += element;
});
element.avg = sum / element.values.length;
});
console.log(data);

答案 3 :(得分:0)
你可以这样:
var data = [{ id: 1, region: "America", country: "USA", values: [1, 2, null, 4] }, { id: 2, region: "America", country: "Canada", values: [3, 4, 5, 6] }, { id: 3, region: "Europe", country: "France", values: [1, 2, 3, 4] }, { id: 4, region: "Europe", country: "Italy", values: [1, 2, 3, 4] }, { id: 5, region: "Europe", country: "Spain", values: [5, 9, 1, 7] }, { id: 6, region: "Europe", country: "Germany", values: [1, 6, 2, 8] }, { id: 7, region: "Europe", country: "Ireland", values: [6, 4, 6, 9] }]
// So that regions are configurable
var regions = ['America', 'Europe'];
var result = [];
// for cases when data is not sorted by id.
var lastIndex = Math.max.apply(null, data.map(function(x){ return x.id}))
regions.forEach(function(r) {
var val = [];
data.forEach(function(c,i){
if(c.region === r && c.values && c.values.length > 0){
c.values.forEach(function(v, i) {
if (!v && v!== 0) return
val[i] = (val[i] || []);
val[i].push(v)
})
}
});
var avg = val.map(function(v){
return (v.reduce(function(p,c){ return p+c }) / v.length);
})
// not pushing into data to prevent extra iterations
result.push({
id: ++lastIndex,
region: r,
country: 'avg',
values: avg
})
});
data = data.concat(result)
console.log(data)
答案 4 :(得分:0)
以下是适用于任意数量地区的一般解决方案。我知道问题是陈旧(2小时),但考虑到方法的简单性。对于每个独特的区域,计算平均值:
1 - 将所有values
数组作为给定区域的一个2d数组
2 - 转置此阵列
3 - 平均每个数组
var data = // your array here
function getArr(data,reg) {
return data.filter(el => el.region === reg)
.map(el => el.values)
}
const xpose = x => x[0].map( (c,i) => x.map( r => r[i] ) )
function avgArr(a) {
a = a.filter(el => el !== null);
return a.reduce((x,y) => x+y) / a.length;
}
function calcAverages(data) {
let i = 1 + Math.max.apply(null, data.map( el => el.id )) // max id
let regions = [...new Set (data.map (a => a.region))] // unique regions
regions.forEach( region => {
data.push( { id: i++,
region: region,
country: "avg",
values: xpose(getArr(data,region)).map(a => avgArr(a))})})
return data
}
// > calcAverages(data)
//
// ...
// { id: 8,
// region: 'America',
// country: 'avg',
// values: [ 2, 3, 4, 5 ] },
// { id: 9,
// region: 'Europe',
// country: 'avg',
// values: [ 2.8, 4.6, 3, 6.4 ] } ]
答案 5 :(得分:0)
我想做如下
var data = [
{id: 1, region: "America", country:"USA", values:[1,2,3,4] },
{id: 2, region: "America", country:"Canada", values:[3,4,5,6] },
{id: 3, region: "Europe", country:"France", values:[1,2,3,4] },
{id: 4, region: "Europe", country:"Italy", values:[1,2,3,4] },
{id: 5, region: "Europe", country:"Spain", values:[5,9,1,7] },
{id: 6, region: "Europe", country:"Germany", values:[1,6,2,8] },
{id: 7, region: "Europe", country:"Ireland", values:[6,4,6,9]}
],
newData = [...new Set(data.map(c => c.region))]
.reduce((f,c,i) => (f.push(...data.filter(o => o.region === c)
.reduce((p,q,_,a) => { p[0].values = q.values.map((v,j) => v/a.length + p[0].values[j]);
p.push(q);
return p;
} ,[{ id: data.length + 1 + i,
region: c,
country: "avg",
values: Array(data[0].values.length).fill(0)
}])),f),[]);
console.log(newData);