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

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个回答

6

我在个人图书馆中使用了以下方法:

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

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

编辑: 要使用它们,只需请求数组的总和或平均值,例如:

[1,2,3].sum() // = 6
[1,2,3].average() // = 2

虽然我会使用for循环而不是reduce来实现sum,但这个实现非常整洁。代码编写得非常干净。 - XDS

4
这是一个快速添加“平均值”命令到javascript中的“Math”对象的方法!
Math.average = function(input) {
  this.output = 0;
  for (this.i = 0; this.i < input.length; this.i++) {
    this.output+=Number(input[this.i]);
  }
  return this.output/input.length;
}

然后我对“Math”对象进行了扩展,以获取总和!
Math.sum = function(input) {
  this.output = 0;
  for (this.i = 0; this.i < input.length; this.i++) {
    this.output+=Number(input[this.i]);
  }
  return this.output;
}

那么你需要做的只是:
alert(Math.sum([5,5,5])); //alerts “15”
alert(Math.average([10,0,5])); //alerts “5”

在哪里放置占位符数组,只需传入您的变量即可(如果输入是数字,则可以是字符串,因为它会解析为数字!)


4

有一个狡猾的方法可以做到这一点,尽管它需要使用(eval())这个广受憎恶的函数。

var sum = eval(elmt.join('+')), avg = sum / elmt.length;
document.write("The sum of all the elements is: " + sum + " The average of all the elements is: " + avg + "<br/>");

我想提供一个“出奇制胜”的选项。你永远不知道,这种狡猾的方法可能会给你(或夺走)一分。


+1 很高效的一行代码!如果是客户端浏览器JavaScript,我唯一可能看到的eval()问题是您需要确保数组仅包含数字(当然,这也适用于此处的所有其他答案)。非数字条目会导致ReferenceErrorSyntaxError崩溃,除了嵌套数字数组引入,导致不正确的数字总数。当然,在服务器端JS中,例如node.js,eval()可能会引入安全问题。但是,对于任何良好结构化的客户端使用,这看起来非常完美。 - user56reinstatemonica8
1
实际上,像这样包含eval()可能存在性能问题-请参阅http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/的性能部分。 - user56reinstatemonica8
哦,我的天啊!eval。哦,我的天啊!哦,我的天啊!哦,我的天啊!- 它简洁地回答了问题,并且与其他答案不同。有些情况下,eval() 是解决问题的唯一方法。但是,嗯,这不是其中之一。 - Stephen Quan

4

我发现Mansilla答案可以很好地处理我的需求,通过使用parseFloat()确保我对浮点数进行求和而不是字符串连接:

let sum = ourarray.reduce((a, b) => parseFloat(a) + parseFloat(b), 0);
let avg = (sum / ourarray.length) || 0;

console.log(sum); // print out sum
console.log(avg); // print out avg

1
我只是在Abdennour TOUMI的回答上进行补充。以下是原因:
1.) 我同意Brad的看法,我认为扩展我们没有创建的对象不是一个好主意。
2.) 在javascript中,array.length并不完全可靠,我更喜欢使用Array.reduce,因为a=[1,3];a[1000]=5;,现在a.length将返回1001
function getAverage(arry){
    // check if array
    if(!(Object.prototype.toString.call(arry) === '[object Array]')){
        return 0;
    }
    var sum = 0, count = 0; 
    sum = arry.reduce(function(previousValue, currentValue, index, array) {
        if(isFinite(currentValue)){
            count++;
            return previousValue+ parseFloat(currentValue);
        }
        return previousValue;
    }, sum);
    return count ? sum / count : 0; 
};

1

首先定义我们计划使用的所有变量。您会注意到,对于numbers数组,我使用了文字表示法[]而不是构造方法array()。此外,我使用了一种更短的方法将多个变量设置为0。

var numbers = [], count = sum = avg = 0;

接下来,我将使用值0到11填充我的空数字数组。这是为了让我回到您的原始起点。请注意,我正在将count++推入数组中。这会推送计数的当前值,然后递增到下一次循环。
while ( count < 12 )
    numbers.push( count++ );

最后,我正在对数字数组中的每个数字执行一个“for each”函数。这个函数将逐个处理一个数字,在函数体内我将其标识为“n”。
numbers.forEach(function(n){
  sum += n; 
  avg = sum / numbers.length;
});

最后,我们可以将sum值和avg值输出到控制台上,以便查看结果:
// Sum: 66, Avg: 5.5
console.log( 'Sum: ' + sum + ', Avg: ' + avg );

在线查看演示,请访问http://jsbin.com/unukoj/3/edit


1
如果有人需要的话 - 这里是一个递归平均值。
在原始问题的背景下,如果您允许用户插入其他值,并且想要“更新”现有平均值而不会产生访问每个元素的成本,则可以使用递归平均值。
/**
 * Computes the recursive average of an indefinite set
 * @param {Iterable<number>} set iterable sequence to average
 * @param {number} initAvg initial average value
 * @param {number} initCount initial average count
 */
function average(set, initAvg, initCount) {
  if (!set || !set[Symbol.iterator])
    throw Error("must pass an iterable sequence");

  let avg = initAvg || 0;
  let avgCnt = initCount || 0;
  for (let x of set) {
    avgCnt += 1;
    avg = avg * ((avgCnt - 1) / avgCnt) + x / avgCnt;
  }
  return avg; // or {avg: avg, count: avgCnt};
}

average([2, 4, 6]);    //returns 4
average([4, 6], 2, 1); //returns 4
average([6], 3, 2);    //returns 4
average({
  *[Symbol.iterator]() {
    yield 2; yield 4; yield 6;
  }
});                    //returns 4

如何实现:

通过保持当前平均值和元素计数来实现。当要包含新值时,将计数增加1,通过 (count-1) / count 缩放现有平均值,并将 newValue / count 添加到平均值中。

优点:

  • 不需要对所有元素求和,这可能会导致无法存储在64位浮点数中的大数字。
  • 如果有额外的值可用,可以“更新”现有的平均值。
  • 可以执行滚动平均而不知道序列长度。

缺点:

  • 会产生更多的除法运算。
  • 不是无限的 - 除非使用BigNumber,否则仅限于 Number.MAX_SAFE_INTEGER 项。

1
Array.prototype.avg=function(fn){
    fn =fn || function(e,i){return e};
    return (this.map(fn).reduce(function(a,b){return parseFloat(a)+parseFloat(b)},0) / this.length ) ; 
};

然后:
[ 1 , 2 , 3].avg() ;  //-> OUT : 2

[{age:25},{age:26},{age:27}].avg(function(e){return e.age}); // OUT : 26

1
在常用浏览器上,你可以使用箭头函数。 avg = [1,2,3].reduce((a,b) => (a+b); 执行此操作100,000次,使用for循环方法和reduce方法的时间差异微不足道。

s=Date.now();for(i=0;i<100000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("100k reduce took " + (Date.now()-s) + "ms.");

s=Date.now();for(i=0;i<100000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("100k for loop took " + (Date.now()-s) + "ms.");

s=Date.now();for(i=0;i<1000000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("1M for loop took " + (Date.now()-s) + "ms.");

s=Date.now();for(i=0;i<1000000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("1M reduce took " + (Date.now()-s) + "ms.");

/* 
 * RESULT on Chrome 51
 * 100k reduce took 26ms.
 * 100k for loop took 35ms.
 * 10M for loop took 126ms.
 * 10M reduce took 209ms.
 */


2
你需要更多地了解console.timeconsole.timeEnd - Abdennour TOUMI
@AbdennourTOUMI 谢谢你的提示,我会记住的... 不过这是非标准的,也不在标准轨道上。https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd - Ray Foss
1
我知道我偏离了原来的问题,但是使用performance.now()来进行性能测量怎么样? - deejbee
@deejbee 这确实可以提供浮点精度...但它也有点啰嗦,而且不太兼容。在实际的代码中使用它来设置标记会很有用。 - Ray Foss

1

仅供娱乐

let avg = [81, 77, -88, 195, 6.8].reduce((a,e,i) => (a*i+e)/(i+1));

console.log(avg)


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