最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does my reduce based average function return NaN? - Stack Overflow

programmeradmin0浏览0评论

Trying to get the average of an array.

Array.prototype.average = function() {
    var sum = 0;
    this.reduce(function(a, b) {
        sum = a + b;
    });
    return sum / this.length;
};

[2, 15, 7].average();

Why does the average function call return NaN?

Trying to get the average of an array.

Array.prototype.average = function() {
    var sum = 0;
    this.reduce(function(a, b) {
        sum = a + b;
    });
    return sum / this.length;
};

[2, 15, 7].average();

Why does the average function call return NaN?

Share Improve this question edited Feb 8, 2015 at 9:55 thefourtheye 239k53 gold badges465 silver badges500 bronze badges asked Feb 8, 2015 at 9:33 Amir RahbaranAmir Rahbaran 2,4302 gold badges22 silver badges28 bronze badges 1
  • 1 From your code, one would assume you are targeting a modern browser. I would suggest using Object.defineProperty to extend the Array.prototype. (and testing that the method doesn't exist first). You could even make the code more generic to work (call/apply) with other objects. – Xotic750 Commented Feb 8, 2015 at 9:45
Add a comment  | 

4 Answers 4

Reset to default 10

Your program didn't work because, a has the accumulated value from the previous function call. The first time, first two values of the array will be used. So sum will become 17 (2 + 15). Since you are not returning anything from the function, undefined will be returned, by default, and that will be used as the value for a, in the next call. So, the evaluation goes like this

a: 2,          b: 15   => 17
a: undefined,  b: 7    => NaN

So, sum will have NaN, since undefined + 7 makes it so. Any numeric operation on NaN, will always give NaN, that is why NaN / this.length, gives you NaN. You can fix your program, just by returning the current value of sum whenever the function is called, so that on the next function call, a will have proper accumulated value.

Array.prototype.average = function() {
    var sum = 0;
    this.reduce(function(a, b) {
        sum = a + b;
        return sum;
    });
    return sum / this.length;
};

But we are not making use of the power and flexibility of reduce here. Here are two important points to consider when using reduce.

  1. reduce accepts a second parameter which says the initial value to be used. Whenever possible, specify that.

  2. The first parameter in the function passed to reduce accumulates the result and that will be returned finally, make use of that. No need to use a separate variable to keep track of the results.

So your code would look better like this

Array.prototype.average = function() {

    var sum = this.reduce(function(result, currentValue) {
        return result + currentValue
    }, 0);

    return sum / this.length;

};

console.log([2, 15, 7].average());
# 8

reduce actually works like this. It iterates through the array and passes the current value as the second parameter to the function and the current accumulated result as the first parameter and the value returned from the function will be stored in the accumulated value. So, the sum is actually found like this

result: 0 , currentValue: 2   => 2    (Initializer value `0`)
result: 2 , currentValue: 15  => 17
result: 17, currentValue: 7   => 24

Since it ran out of values from the array, 24 will be returned as the result of the reduce, which will be stored in sum.

Your anonymous addition function does not return any value, reduce works with functions that return a value:

Try:

Array.prototype.average = function() {
    var sum = this.reduce(function (a, b) {
        return a + b;
    }, 0);
    return sum/this.length;
};

Another possibility is that your array contains strings instead of numbers, thus you could want to coerce them to numbers with return (+a) + (+b); as if you have "10.0" and "20.0", adding them together gives "10.020.0", which divided by any number again gives NaN.

You already have the answer to your question provided by others, but I thought I'd just expand on my comment with an example.

if (!Array.prototype.average) {
    Object.defineProperty(Array.prototype, 'average', {
        value: function () {
            if (typeof this === 'undefined' || this === null) {
                throw new TypeError('Cannot convert argument to object');
            }

            var object = Object(this);

            return [].reduce.call(object, function (result, currentValue) {
                return +(currentValue) + result;
            }, 0) / object.length;
        }
    });
}

var out = document.getElementById('out');

out.textContent += [2, 15, 7].average() + '\n';
out.textContent += [].average.call({
    0: '2',
    1: '15',
    2: '7',
    length: 3
}) + '\n';
out.textContent += [].average.call('123') + '\n';
<pre id="out"></pre>

Whatever value is returned from reduce, it becomes the first parameter in the next call, so you have to return something. Also if you're extending prototypes on purpose, make sure to check for existence first so you're not overriding someone else's method.

There is no need to create any other variables in the function body as it can all be implemented in one line.

if(!Array.prototype.average) {
  Array.prototype.average = function() {
    return this.reduce(function(a, b){ return a + b; }) / this.length;
  };
}

Also note that the second parameter of reduce isn't very useful when summing numbers, unless you're trying to sum up from a number other than zero, which isn't exactly summing a set of numbers.

There is more info on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

发布评论

评论列表(0)

  1. 暂无评论