Let’s see what techniques we can apply to improve this code to be something more usable.
First, indent the code so that the different structures are easier to see.
Next, replace one-line if and for statements with braces surrounding the code block instead.
The variable initialization can be looked at next. The following can be confusing for some to understand:
var mean = Math.average = function() {
In keeping with the rest of the code, we’ll assign the function to just one variable and then later on assign Math.average.
In fact, why are they being assigned to variables? They’re just functions.
function mean() {
...
};
Math.average = mean;
The multiple assignments on one line can be simplified:
var cnt, tot, i;
cnt = arguments.length;
tot = i = 0;
Using a series of var statements helps to make everything there clearer. Also, we don’t need to restrict ourselves to three character variable names. They can be spelled out and made crystal clear.
var count = arguments.length;
var total = 0;
var i = 0;
Next, we have a while loop, with an increment happening inside.
while (i < count;) {
total += arguments[i++];
}
It’s far easier to understand how that section works if we make it a for loop instead. Any tiny fraction of performance benefit is lost when coders have to stop and think about what’s happening there.
for (i = 0; i < count; i += 1) {
total += arguments[i];
}
And if we don’t mind using ES6 techniques, we could even use Array.from along with Array.reduce to help us sum up the total.
total = Array.from(arguments).reduce(function (total, num) {
return total + num;
}, total);
Prefixing something with the plus symbol is a hack:
(array[i] !== +array[i])
and should be replaced with easier to understand coding techniques instead. Taking an overall look at the use of infinity in the median function, this can all be vastly simplified by the use of the splice method.
for (i = array.length - 1; i >= 0; i -= 1) {
if (array[i] !== Number(array[i])) {
array.splice(i, 1);
}
}
And if we don’t mind using ES5 techniques like Array.filter, this can all be simplified to:
array = array.filter(function (num) {
return num === Number(num);
}
Next with the mode function, code that requires you to add “~” symbols all over the place and then needs the use of regular expressions such as /(~\-?\d+~)\1*/g
is too complex for its own good.
We can improve all this by keeping a counter tally for each number that we see:
for (i = 0; i < array.length; i += 1) {
num = array[i];
if (num === Number(num)) {
if (counter[num] === undefined) {
counter[num] = 0;
}
counter[num] += 1;
}
and tracking if that amount is the most that we’ve seen. Whenever the max is increased we reset the array with that number:
if (counter[num] > max) {
max = counter[num];
modes = [num];
}
and if any counter matches that max we can just add it on to that array.
} else if (counter[num] === max) {
modes.push(num);
}
This way the code is a lot clearer than it would be otherwise, allowing us to more easily understand how it works too.
The resulting code after this cleanup is:
function mean() {
var count = arguments.length;
var total = 0;
var i;
for (i = 0; i < count; i += 1) {
total += arguments[i];
}
return total / count;
}
function median() {
function numA(a, b) {
return (a - b);
}
var array = Array.prototype.slice.call(arguments);
var i;
for (i = array.length - 1; i >= 0; i -= 1) {
if (array[i] !== Number(array[i])) {
array.splice(i, 1);
}
}
array.sort(numA);
return array[Math.floor(array.length / 2)];
}
function mode() {
var array = Array.prototype.slice.call(arguments);
var max = 0;
var counter = [];
var modes = [];
var i;
var num;
for (i = 0; i < array.length; i += 1) {
num = array[i];
if (num === Number(num)) {
if (counter[num] === undefined) {
counter[num] = 0;
}
counter[num] += 1;
}
if (counter[num] > max) {
max = counter[num];
modes = [num];
} else if (counter[num] === max) {
modes.push(num);
}
}
return modes;
}
Math.average = mean;