Home > Net >  How can I interpolate zeroes sequences in a number array?
How can I interpolate zeroes sequences in a number array?

Time:08-27

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
  
];

CodePudding user response:

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);

CodePudding user response:

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)

CodePudding user response:

I would keep track of the start and end values when looping over looking for non zeros. When it is the start the starting index will not be set so you know it needs to be the first value. You can loop one extra index and you know it will be the end. For the others it will be the step like you did.

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
]; 

let startIndex = -1;

for (var i = 0; i <= sequence.length; i  ) {

if (sequence[i] !== 0) {
    if (i - startIndex > 1) {

      let func;
      const startVal = sequence[startIndex];
      const endVal = sequence[i];

      if (startIndex === -1) {
        func = () => endVal;
      } else if (i === sequence.length) {
        func = () => startVal;
      } else {
        func = (j) => {
            return startVal   (endVal - startVal) / (i - startIndex) * j;
        }
      }

      for (let j = 1; j < i - startIndex; j  ) {
        sequence[j   startIndex] = func(j);
      }
    }
    
    startIndex = i;
  }
}

console.log(sequence);

Other way is set the start and end and do the calculation

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
]; 

let startIndex = -1;
for (var i = 0; i <= sequence.length; i  ) {

  if (sequence[i] !== 0) {
    if (i - startIndex > 1) {

      const startVal = startIndex === -1 ? sequence[i] : sequence[startIndex];
      const endVal = i === sequence.length ? startVal : sequence[i];

      const func = (j) => {
        return startVal   (endVal - startVal) / (i - startIndex) * j;
      }

      for (let j = 1; j < i - startIndex; j  ) {
        sequence[j   startIndex] = func(j);
      }
    }

    startIndex = i;
  }
}

console.log(sequence);

CodePudding user response:

Haven't checked my sample, but you should see the way I guess..

    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
];
let temporaryZerosArray = [];
for (const [id, value] of sequence.entries()) {
   if (value) {
      if (!temporaryZerosArray.length) continue;
      temporaryZerosArray.splice(0, temporaryZerosArray.length - 2);
      let [one, two] = temporaryZerosArray;
      let lf = sequence[one-1]
      let rf = sequence[two 1]
      if (
         (!!lf && lf > 0) &&
         (!!rf && rf > 0)
      ) interpolateValues(sequence, one,two, lf , rf );
      temporaryZerosArray = [];
      continue
   }
   temporaryZerosArray.push(id);
}

CodePudding user response:

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;
}
  • Related