let's say I have an arbitrary number sequence
let sequence = [
0, 0, 0, 0, 0,
12, 64, 9, 6,
0, 0, 0,
25, 79, 57, 13, 39,
0, 0,
7, 7,
0, 0, 0, 0, 0,
49,
0
];
I need to replace all zeroes with interpolation given from non-zeroes neighbours, so the output would be
let output = [
12, 12, 12, 12, 12,
12, 64, 9, 6,
10.75, 15.5, 20.25,
25, 79, 57, 13, 39,
28.3333, 17.6666,
7, 7,
14, 21, 28, 35, 42,
49,
49
];
While firs zeroes [0, 4] doesn't have left neighbour all their values have to be 12, while last zero has only right resident 49, it would be just 49.
For me, it doesn't really a problem to fill parts where both left and right neighbours are presented, however I'm looking for an universal and elegant solution for this task.
const interpolateValues = (array, index0, index1, left, right) => {
let n = index1 - index0 + 1;
let step = (right - left) / (n + 1);
for(let i = 0; i < n; i++){
array[index0 + i] = left + step * (i + 1);
}
}
const findZerosSequences = (array) => {
var counter = 0;
var index = 0;
var result = [];
for (let i = 0; i < array.length; i++) {
if (array[i] === 0) {
index = i;
counter++;
} else {
if (counter !== 0) {
result.push([index - counter + 1, index]);
counter = 0;
}
}
}
if (counter !== 0) { result.push([index - counter + 1, index]); }
return result;
}
let sequence = [
0, 0, 0, 0, 0,
12, 64, 9, 6,
0, 0, 0,
25, 79, 57, 13, 39,
0, 0,
7, 7,
0, 0, 0, 0, 0,
49,
0
];
//[[0,4], [9, 11], [17, 18], [21, 25], [27, 27]]
let zeroes = findZerosSequences(sequence);
for(let i = 0; i < zeroes.length; i++){
let lf = sequence[zeroes[i][0] - 1];
let rf = sequence[zeroes[i][1] + 1];
if(lf !== undefined && rf !== undefined && lf > 0 && rf > 0){
interpolateValues(sequence, zeroes[i][0], zeroes[i][1], lf, rf);
}
}
console.log(sequence);
let output = [
12, 12, 12, 12, 12,
12, 64, 9, 6,
10.75, 15.5, 20.25,
25, 79, 57, 13, 39,
28.3333, 17.6666,
7, 7,
14, 21, 28, 35, 42,
49,
49
];
If anyone would be interested in spaghetti instead of a valid answer :)
const sequence = [
0, 0, 0, 0, 0,
12, 64, 9, 6,
0, 0, 0,
25, 79, 57, 13, 39,
0, 0,
7, 7,
0, 0, 0, 0, 0,
49,
0
]
const output = sequence.join(',')
.replace(/^([0,]+)(\d+)/, (_, zeros, number) => {
const n = zeros.match(/0/g).length
return (number + ',').repeat(n) + number
})
.replace(/([^0,]+),([0,]+)([^0,]+)/g, (_, number1, zeros, number2) => {
const n = zeros.match(/0/g).length
const diff = +number2 - +number1
const step = diff / (n + 1)
return number1 + ',' + [...Array(n).keys()].map(i => {
const val = +number1 + (i + 1) * step
return Math.floor(val * 10000) / 10000
}) + ',' + number2
})
.replace(/(\d+)([,0]+)$/, (_, number, zeros) => {
const n = zeros.match(/0/g).length
return number + (',' + number).repeat(n)
}).split(',').map(Number);
console.log(output)
You almost got it, let the interpolateValues worry about those edge cases which are easily resolved.
let sequence = [
0, 0, 0, 0, 0,
12, 64, 9, 6,
0, 0, 0,
25, 79, 57, 13, 39,
0, 0,
7, 7,
0, 0, 0, 0, 0,
49,
0
];
const interpolateValues = (array, index0, index1, left, right) => {
if (left === null) left = right;
if (right === null) right = left;
if (left === null && right === null) left = right = 0;
let n = index1 - index0 + 1;
let step = (right - left) / (n + 1);
for (let i = 0; i < n; i++) {
array[index0 + i] = left + step * (i + 1);
}
}
const findZerosSequences = (array) => {
var counter = 0;
var index = 0;
var result = [];
for (let i = 0; i < array.length; i++) {
if (array[i] === 0) {
index = i;
counter++;
} else {
if (counter !== 0) {
result.push([index - counter + 1, index]);
counter = 0;
}
}
}
if (counter !== 0) {
result.push([index - counter + 1, index]);
}
return result;
}
let zeroes = findZerosSequences(sequence);
for (let i = 0; i < zeroes.length; i++) {
let lf = zeroes[i][0] - 1 >= 0 ? sequence[zeroes[i][0] - 1] : null;
let rf = zeroes[i][1] + 1 < sequence.length ? sequence[zeroes[i][1] + 1] : null;
interpolateValues(sequence, zeroes[i][0], zeroes[i][1], lf, rf);
}
console.log(sequence);
Here's a solution that accepts your input array and returns the interpolated output array. I put comments inline with the code to explain how it works. This solution also behaves correctly for arrays of all zeros.
function interpolateArray(input) {
let output = []; // New array for output
let zeros = 0; // Count of sequential zeros
let start = 0; // Starting number for interpolation
for (let i = 0; i < input.length; i++) { // Loop through all input values
let value = input[i]; // Current input value
if (value === 0) zeros++; // If value is zero, increment the zero count
else { // If the value is non-zero...
if (start === 0) start = value; // If the starting value is zero, set start to current non-zero value
if (zeros) { // If there are zeros accumulated...
let step = (value - start) / (zeros + 1); // Compute the step value (current value, minus start, divided by total steps)
for (let j = 1; j <= zeros; j++) output.push(start + (j * step)); // For each zero, push the stepped value to output
zeros = 0; // Reset zero count
}
start = value; // Store the current value as the new start
output.push(start); // Push the current non-zero value to output
}
}
for (let j = 0; j < zeros; j++) output.push(start); // If there are accumulated zeros, that means they were trailing. Push last non-zero value to output for each
return output; // Return the output
}
Update:
Just for fun, I tightened up the code a bit so the function is more compact. It works exactly the same.
function interpolateArray(input) {
let output = [], zeros = 0, start = 0;
input.forEach(value => {
if (value) {
start = start || value;
if (zeros) {
let step = (value - start) / (zeros + 1);
while (zeros--) output.push(start += step);
zeros = 0;
}
output.push(start = value);
} else zeros++;
});
while (zeros--) output.push(start);
return output;
}