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
?
4 Answers
Reset to default 10Your 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
.
reduce
accepts a second parameter which says the initial value to be used. Whenever possible, specify that.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
Object.defineProperty
to extend theArray.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