The following would be my solution in JS. I first find the minimum gap and then try to find how many of that would fit in-between each item and process accordingly without altering the original values.
Obviously for this algorithm to work the input array must be sorted in ascending order.
var arr = [0, 99, 299, 498, 901],
gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])), // Find the minimum gap
res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap); // Find howmany gaps are inbetween according to the minimum gap
g = Math.round((c-p[p.length-1])/n); // Calculate the average gap top apply
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
},[]);
console.log(res);
Explanation:
gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])),
First we set up a new array in size one less than the input array. (Array(arr.length-1)) first we initialize (.fill()) it with undefined elements and then .map() every element with arr[i+1]-arr[i]. So now we have the gaps array. Then we spread it into a Math.min() function as arguments. It's the Math.min(...Array( part. So now we have the minimum gap as 99 in the above given case.
res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap);
g = Math.round((c-p[p.length-1])/n);
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
},[]);
.reduce() part is slightly tough looking but it's easy. Our .reduce() operation takes a function as it's argument (mostly known as a callback function) and runs it with every iteration over the array items. This callback function is the part which starts with (p,c,i) => {... }. This is an arrow function. Which is essentially same with normal functions. x => x means function(x) { return x;} or x => {return x;}. In our case since we use braces to define the body of our function (due to multiple statements) we will have to use a return instruction.
Our .reduce() uses an initial value which is an empty array. It's the ,[]); part at the very end. The callback function, which reduce will invoke per array item, will be passed three arguments (p,c,i) The initial empty array gets assigned to the p (previous) argument, the current item gets assigned to the c argument and the current index gets assigned to the i argument per call.
In the body of our callback we define 2 variables. n and g.
n = Math.round((c-p[p.length-1])/gap);
p[p.length-1] returns the last element of the p array. So in the first turn; when i = 0, p[0] is undefined and Math.round((c-p[p.length-1])/gap); is a NaN (Not a Number) but we don't care because;
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
The ternary conditional means that;
result = condition ? if true do this
: if false do this
So as you see depending on the condition it does either one of the instructions and returns the result. In our case the result is returned as the value of p.
So in our case if i == 0 (false value in JS) then only do p.concat(c) and return the new p value and continue with the next iteration (invoke callback with the new p, c and i values.
If i is not false (any value other than 0) then do like
p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
Which means create an array in the size to take the gap many interim elements, initialize the array with undefineds and map each element with p[p.length-1] + (i+1)*g and concatenate this array to the p array and append c to the very end and then return the p array.
One thing to remind: p.concat(whatever...) instruction would return a new array consisting of the elements of p and the "items" of the arrays included as argument or the items itself included ar argument. I mean;
[1,2,3].concat([4,5,6],[7,8],9) would result [1,2,3,4,5,6,7,8,9]
So this should explain it.