This is great example to practice identifying the right abstraction.
In testing to see if an array is geometric or arithmetic series, what you are really doing is testing if a particular mathemtical relation holds for each element. That is, the heart of your program is in these 2 lines:
if(arr[i+1]-arr[i]===arr[i+2]-arr[i+1]){
//...
else if(arr[i+1]/arr[i] ===arr[i+2]/arr[i+1]){
Let's go ahead and extract just these relations into their own functions:
// This tells if an arithmetic relation holds at a particular index of the array
// The function signature here is chosen to work with javascript's every method
// The reason will become clear below.
function isArithmeticAtIndex(elm, i, arr) {
if (i < 2) return true; // trivially true when it can't be false
return arr[i] - arr[i-1] === arr[i-1] - arr[i-2];
}
function isGeometricAtIndex(elm, i, arr) {
if (i < 2) return true;
if (arr[i-1] == 0 || arr[i-2] == 0) return false; // can't divide by 0
return arr[i] / arr[i-1] === arr[i-1] / arr[i-2];
}
Now we'll create a generic function that tells us if a particular mathematical relation holds at every elment of an array:
function hasSeriesRelation(arr, relationFn) {
// geometric and arithmetic series (and most others)
// don't make sense with less than 3 elements
if (arr.length < 3) return false;
return arr.every(relationFn);
}
At this point, we are basically done. We can use what we've written as follows:
a = [1,2,3,4];
hasSeriesRelation(a, isArithmeticAtIndex);
// => true
a = [1,2,3,5];
hasSeriesRelation(a, isArithmeticAtIndex);
// => false
a = [1,2,4,8];
hasSeriesRelation(a, isGeometricAtIndex);
// => true
a = [1,2,4,9];
hasSeriesRelation(a, isGeometricAtIndex);
// => false
But writing hasSeriesRelation(a, isGeometricAtIndex)
is cumbersome, so let's wrap these up in their own functions, and make them array methods:
Array.prototype.isArithmeticSeries = function() { return hasSeriesRelation(this, isArithmeticAtIndex) };
Array.prototype.isGeometricSeries = function() { return hasSeriesRelation(this, isGeometricAtIndex) };
And now we can write things like:
[1,2,4,8].isGeometricSeries();
[1,2,3,4].isArithmeticSeries();
Side Note: Some consider extending Array.prototype
bad practice. I think it can be okay depending on your circumstances. See this post for more discussion. In any case, it's only marginally relevant to this discussion, because you can easily rewrite the above to functions with signatures like function isArithmeticSeries(arr)
Finally, note that we have gained the ability to quickly and easily add new types of series. For example, we might want to identify Fibonacci-like series:
function isFibonacciAtIndex(elm, i, arr) {
if (i < 2) return true; // trivially true when it can't be false
return arr[i] === arr[i-1] + arr[i-2];
}
a = [1,1,2,3,5,8];
hasSeriesRelation(a, isFibonacciAtIndex);
// => true