如何计算数组中元素的总和和平均值?

284

我遇到了将数组所有元素相加并计算它们的平均值的问题。我该怎么做,并在当前代码中实现它?如下所示,这些元素应该被定义。

<script type="text/javascript">
//<![CDATA[

var i;
var elmt = new Array();

elmt[0] = "0";
elmt[1] = "1";
elmt[2] = "2";
elmt[3] = "3";
elmt[4] = "4";
elmt[5] = "7";
elmt[6] = "8";
elmt[7] = "9";
elmt[8] = "10";
elmt[9] = "11";

// Problem here
for (i = 9; i < 10; i++){
  document.write("The sum of all the elements is: " + /* Problem here */ + " The average of all the elements is: " + /* Problem here */ + "<br/>");
}   

//]]>
</script>

62
var elmt = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 这样会更好。 - James M
35个回答

665

我认为更优雅的解决方案:

const sum = times.reduce((a, b) => a + b, 0);
const avg = (sum / times.length) || 0;

console.log(`The sum is: ${sum}. The average is: ${avg}.`);

26
使用累积移动平均数,您可以在reduce函数中完成所有操作(无需 sum/times.length 步骤):var avg = times.reduce(function(p,c,i){return p+(c-p)/(i+1)},0);。请注意保持原文意思并提高可读性。 - zamnuts
6
我支持你的观点,我认为Thor84no的评论完全不合适 - 如果一个地方认为array.reduce太复杂而开除我,我会很高兴。然而,我也不会使用你的代码,因为简单来说,它在每一步都有一个除法和两个额外的加法,这永远不会比最后一步的单个除法更有效率。 - gotofritz
19
在ES2015中,这样写更加优雅:times.reduce((a,b) => (a+b)) / times.length;,意思是将times数组中的所有元素求和并除以数组长度。 - Ray Foss
1
@furf 需要注意的是,知道自己在做什么的编译器可以使reduce版本比循环版本更快。在撰写此评论时,在Firefox中,reduce版本似乎更快,因此其他编译器也会采用这种方式,这只是时间问题。 - dmcontador
12
在5年后的现在,许多现代浏览器中,更优雅的方法"reduce()" 在速度上好像更快(或者和之前一样快)。在Chrome 65和大多数Firefox中,使用reduce进行测试效果更佳。 - Sir Robert
显示剩余6条评论

170

ES6

const average = arr => arr.reduce( ( p, c ) => p + c, 0 ) / arr.length;
    
const result = average( [ 4, 4, 5, 6, 6 ] ); // 5
    
console.log(result);


4
改进后的代码:Array.prototype.average = function() { var sum = 0, j = 0; for (var i = 0; i < this.length && isFinite(this[i]); i++) { //改变逗号为&& sum += parseFloat(this[i]); ++j; } return j ? sum / j : 0; };该段代码用于计算数组中数值元素的平均值,将其添加到Array对象的原型上。在循环中,使用isFinite检测元素是否为有限数字,如果是,则将其转换成浮点数并加入到总和中,同时增加计数器。最后,如果计数器不为零,计算平均值并返回;否则返回0。 - Gilad Peleg
64
请不要扩展非由您的代码创建的对象。 - Brad
1
UPDATE 不适用于:["RER","4","3re",5,new Object()]。.average(); 返回0,而不是预期的4.5。 - Luckylooke
8
当长度为零时,这将会崩溃。 - Jimmy Kane
3
不,它将返回“NaN”。 - bzeaman
显示剩余3条评论

159
var sum = 0;
for( var i = 0; i < elmt.length; i++ ){
    sum += parseInt( elmt[i], 10 ); //don't forget to add the base
}

var avg = sum/elmt.length;

document.write( "The sum of all the elements is: " + sum + " The average is: " + avg );

只需遍历数组,由于您的值是字符串,因此首先必须将它们转换为整数。平均值只是值的总和除以值的数量。


14
我唯一想要改进的是将for(var i = 0; i < elmt.length; i++;)替换为for(var i = 0, l = elmt.length; i < l; i++) - Dan D.
5
不想成为那个人或跑题,但在parseInt()中包含基数参数是一个好习惯。我的意思是 sum += parseInt(elmt[i], 10);,假设 elmt[i] 是以十进制为基数的。链接 - Ryder Brooks
9
注意避免除以0的情况(elmt.length可能为0)。 - caulitomaz
2
@BramVanroy 简短的故事:不,它并不会每次都设置“l”。长故事:DanD的解决方案是为了提高性能/速度,因为在每次迭代中检查数组的长度比实际存储长度并在每次迭代中引用它要慢。 - CatalinBerta
9
@VitoGentile和DanD因性能而使代码变丑,然而现代浏览器会将其编译消除,这并不是最佳实践。 - nauti
显示剩余4条评论

51

使用 reduce 和 ES6 计算平均值(均值):

const average = list => list.reduce((prev, curr) => prev + curr) / list.length;

const list = [0, 10, 20, 30]
average(list) // 15

1
与基于简单for循环和sum变量的加法相比,非常非常慢。http://jsben.ch/0xUus - PirateApp
这里有25%的差异。我想还不错。 - Michael
不确定为什么基准测试使用具有数字键的对象而不是数组。这是使用实际数组的相同基准测试 https://jsben.ch/SGU2d 。对我来说(Chrome 103-Linux),reduce实际上是最快的。 - Kevin Peña

37

求平均数的最短一行代码

const avg = arr => arr.reduce((acc,v,i,a)=>(acc+v/a.length),0);

求和的最短一句话代码

const sum = arr => arr.reduce((a,b)=>a+b);

2
如果你将返回值切换为 a+(v-a)/(i+1),你可以保持相同的字符数,并使其在数值上更加稳定,特别是相对于所有通过 array.length 进行除法的答案。 - Erik

24

让我们想象我们有一个像这样的整数数组:

var values = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];

平均数使用下列公式计算

A= (1/n)Σxi (其中 i = 1 到 n ) ... 因此:x1/n + x2/n + ... + xn/n

我们将当前值除以值的数量,将前一个结果添加到返回值中。

reduce 方法的签名为

reduce(callback[,default_previous_value])

reduce回调函数接受以下参数:

  • p:上一次计算的结果
  • c:当前值(来自当前索引)
  • i:当前数组元素的索引值
  • a:当前被减少的数组

第二个reduce参数是默认值...(在数组为空的情况下使用)。

因此,求平均值的reduce方法将如下所示:

var avg = values.reduce(function(p,c,i,a){return p + (c/a.length)},0);

如果您愿意,您可以创建一个单独的函数

function average(p,c,i,a){return p + (c/a.length)};
function sum(p,c){return p + c)};

然后只需参考回调方法的签名即可

var avg = values.reduce(average,0);
var sum= values.reduce(sum,0);

或者直接增强数组原型..

Array.prototype.sum = Array.prototype.sum || function (){
  return this.reduce(function(p,c){return p+c},0);
};

在调用reduce方法的每个步骤中都可以将值进行分割。

Array.prototype.avg = Array.prototype.avg || function () {
  return this.reduce(function(p,c,i,a){return p+(c/a.length)},0);
};

或者更好的是,使用先前定义的Array.protoype.sum()方法,通过仅调用一次除法来优化过程 :)

Array.prototype.avg = Array.prototype.avg || function () {
  return this.sum()/this.length; 
};

然后,在任何作用域的Array对象上:

[2, 6].avg();// -> 4
[2, 6].sum();// -> 8

NB:在我看来,返回NaN的空数组比返回0更正确,并且在特定的用例中可能会很有用。


22

通常使用一行代码实现平均值缩减的方式如下

elements.reduce(function(sum, a,i,ar) { sum += a;  return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);

专门回答问题

elements.reduce(function(sum, a,i,ar) { sum += parseFloat(a);  return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);

一种高效的版本类似于

elements.reduce(function(sum, a) { return sum + a },0)/(elements.length||1);
了解 JavaScript 数组减少(Reduce)只需1分钟 http://www.airpair.com/javascript/javascript-array-reduce 正如 gotofritz 指出的那样,Array.reduce 会跳过未定义的值。因此,这是一个修复方法:
(function average(arr){var finalstate=arr.reduce(function(state,a) { state.sum+=a;state.count+=1; return state },{sum:0,count:0}); return finalstate.sum/finalstate.count})([2,,,6])

2
elements.length!=0?elements.length:1 可以这样写:elements.length || 1这种写法更短,更易读。 - 4rzael
2
请注意,这仅适用于没有空缺的数组。 - gotofritz
2
建议:添加未压缩(实际上只是漂亮打印的)版本的代码片段,以便人们可以阅读它们。我可以自己编辑它们,但我不喜欢在那种程度上更改别人的答案。 - Justin Morgan

21

在数学部分,您还可以使用lodash、_.sum(array)和_.mean(array)(还有其他方便的工具)。

_.sum([4, 2, 8, 6]);
// => 20
_.mean([4, 2, 8, 6]);
// => 5

mean不是一个函数吗? - Gobliins

6
在支持ES6的浏览器中,这个polyfill可能会有所帮助。
Math.sum = (...a) => Array.prototype.reduce.call(a,(a,b) => a+b)

Math.avg = (...a) => Math.sum(...a)/a.length;

您可以在Math.sumMath.avgMath.max之间共享相同的调用方法,例如:
var maxOne = Math.max(1,2,3,4) // 4;

你可以使用Math.sum来进行计算。
var sumNum = Math.sum(1,2,3,4) // 10

如果您有一个需要求和的数组,您可以使用以下方式:

var sumNum = Math.sum.apply(null,[1,2,3,4]) // 10

就像

var maxOne = Math.max.apply(null,[1,2,3,4]) // 4

不要向 Array.prototype 添加方法,我认为将它们添加到 Math 对象中是更好的选择 - 它们都是“数字处理程序”。 - Oboo Cheng
你不能在箭头函数中像那样使用 "this"。 它会抛出 "TypeError: this.sum 不是一个函数",因为 "this" 将引用箭头函数被调用时的上下文对象,即在此示例中是 window。 将其改为 "Math.sum(...)"。 - FireFuro99
1
@FireFuro99 你说得对,我也不知道为什么这么长时间都没有更新。 - Oboo Cheng

6

虽然不是最快的方法,但是可以使用 map() 和 reduce() 方法来实现最短且一行的代码:

var average = [7,14,21].map(function(x,i,arr){return x/arr.length}).reduce(function(a,b){return a + b})

1
也许更短 [7,14,21].reduce((avg,e,i,arr)=>avg+e/arr.length,0); 但在空数组上有不同的行为。如果你希望在空数组上得到空结果:[].reduce((a,e,i,arr)=>(a?a:0)+e/arr.length,"") - Valen
@Tobiq map和reduce自ES5(2009年标准化)以来就非常标准化了。 - Johann Echavarria
for (var avg = 0, length = i = arr.length; i--;) avg += arr[i] / length 更清晰、更短、更快。 - Tobiq
2
@Tobiq,你的代码一点也不清晰。这种类型的循环一眼看上去就难以理解。 - Porkopek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接