0

I'm trying to fill an array with missing intermediate data

My data input is like this

var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];

I wanna fill the array with missing value but I need to respect this rule:

  1. The 1st value on 2d array must be the next sequence number, so 5.23 ... 5.24 ... 5.25 ...
  2. The 2nd value on 2d array must be the same element from the i+1 value

So the results in this case would be

var data = [[5.23,7],[5.24,7],[5.25,7],[5.26,7],[5.27,7],[5.28,7],[5.29,8],[5.30,8],[5.31,8],[5.32,8],[5.33,8],[5.34,8],[5.35,8]];

This little piece of code works, but I don't know how to put in loop and how to write a while loop that pass every time the new length of the array

var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];

if (data[1][0]-data[0][0] > 0.01) {
    data.push([data[0][0]+0.01,data[1][1]]);
    data.sort(function (a, b) { return a[0] - b[0]; });
} else {
    check the next element
}

console.log(data);

Any idea?

Jorman Franzini
  • 329
  • 1
  • 3
  • 17

3 Answers3

1

Array.prototype.reduce() is sometimes handy to extend the array. May be you can do as follows;

var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]],
 newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
                                                                                            .map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
                                      : [c],[]);
console.log(newData);

var data = [[1.01,3],[1.04,4],[1.09,5],[1.10,6],[1.15,7]],
 newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
                                                                                            .map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
                                      : [c],[]);
console.log(newData);
Redu
  • 25,060
  • 6
  • 56
  • 76
1

Here's another idea... I thought it might feel more natural to loop through the sequence numbers directly.

Your final array will range (in this example) from 5.23 to 5.35 incrementing by 0.01. This approach uses a for loop starting going from 5.23 to 5.35 incrementing by 0.01.

key points

  • Rounding: Work in x100 then divide back down to avoid floating point rounding issues. I round to the neared hundredth using toFixed(2) and then converting back to a number (with leading + operator).
  • Indexing: Recognizing 5.23 is the zero index with each index incrementing 1/100, you can calculate index from numerical values, ex. 100*(5.31-5.23) equals 8 (so 5.31 belongs in output[8]).
  • 2nd values: given a numerical value (ex. 5.31), just find the first element in the data array with a higher 1st value and use its 2nd value - this is a corollary of your requirement. Because 5.31 <= 5.28 is false, don't use 7 (from [5.28,7]). Because 5.31 <= 5.32 is true, use 8 (from [5.32,8]).

EDIT

I improved the performance a bit - (1) initialize output instead of modifying array size, (2) work in multiples of 100 instead of continuously rounding from floating point to hundredths.

I ran 5000 iterations on a longer example and, on average, these modifications make this approach 3x faster than Redu's (where the original was 2x slower).

var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];

var output = Array((data[data.length-1][0]-data[0][0]).toFixed(2)*100+1)

function getIndex(value){
   return (value-data[0][0]*100)
}
  
for( var i = 100*data[0][0]; i <= 100*data[data.length-1][0]; i++ ){
  output[getIndex(i)] = [i/100, data.find( d => i <= 100*d[0] )[1]]
}

//console.log(output)





// Performance comparison
function option1(data){
  let t = performance.now()

  var output = Array((data[data.length-1][0]-data[0][0]).toFixed(2)*100+1)

  function getIndex(value){
     return (value-data[0][0]*100)
  }
  
  for( var i = 100*data[0][0]; i <= 100*data[data.length-1][0]; i++ ){
    output[getIndex(i)] = [i/100, data.find( d => i <= 100*d[0] )[1]]
  }

  return performance.now()-t
}

function option2(data){
  let t = performance.now()
  
  newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
                                                                                            .map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
                                      : [c],[]);
  return performance.now()-t
}

var testdata = [[1.13,4],[2.05,6],[5.23,7],[5.28,7],[5.32,8],[5.35,8],[8.91,9],[10.31,9]];
var nTrials = 10000;

for(var trial=0, t1=0; trial<=nTrials; trial++) t1 += option1(testdata)
for(var trial=0, t2=0; trial<=nTrials; trial++) t2 += option2(testdata)

console.log(t1/nTrials) // ~0.4 ms
console.log(t2/nTrials) // ~0.55 ms
Steve Ladavich
  • 3,472
  • 20
  • 27
  • Good Idea, tnx. But, for performance purpose, I think that the @Redu aproach is better – Jorman Franzini Oct 17 '16 at 20:17
  • @JormanFranzini, I made some updates to improve the performance. By my estimate, it should be about 3x faster than Redu's answer. – Steve Ladavich Oct 18 '16 at 16:48
  • Tnx, I tried but, seems to be half slow than the Reud's one. I tried with a bounch of data, your take 10/11ms and redu's 5/6ms – Jorman Franzini Oct 18 '16 at 18:20
  • @JormanFranzini; strange. I added my performance check in. Maybe I'm doing something wrong?? While I'm not seeing a 3x increase (like I was before), my answer is still faster - I'm getting 0.4 ms to 0.55ms in my test example. I thought it might have something to do with the sparsity of the array (ie. output.length >>> input.length), but I'm getting similar timing differences for every example I try – Steve Ladavich Oct 18 '16 at 19:53
  • Tnx for your reply, maybe I'm wrong, I use this https://repl.it/EAsK/0 to test your code, and this https://repl.it/EAsG/0 to test the Reud's one! Like I said, maybe I'm in the wrong way. Let me know – Jorman Franzini Oct 19 '16 at 18:52
  • @JormanFranzini, looks like you're actually testing my original code. I had made an edit to improve the performance :) – Steve Ladavich Oct 19 '16 at 19:48
  • @JormanFranzini, also, here are some things to consider for your performance testing. (1) replace `new date` with `performance.now()` to get more precise timing http://stackoverflow.com/questions/30795525/performance-now-vs-date-now. (2) run many iterations and take an average - my current answer includes a performance comparison that calculates average performance of 10k iterations (it takes a few seconds). – Steve Ladavich Oct 19 '16 at 19:53
  • Yep, you're right! I don't use iteration because I need only for few numbers, but your solution now works better than Reud's one! Tnx for your support! – Jorman Franzini Oct 21 '16 at 16:45
0

I propose this solution :

var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];
var res = [];
data.forEach((item, index, arr) => {
   res.push(item);
   var temp = item[0];
   while (arr[index+1] && arr[index+1][0]-temp > 0.01){
      temp += 0.01;
      res.push([temp, arr[index+1][1]]);
   }
});
console.log(res); 
kevin ternet
  • 4,514
  • 2
  • 19
  • 27
  • Tnx I'll try. I think the Redu idea to multiply by 100 and add 1, is cool. Actually your idea return [ [ 5.23, 7 ], [ 5.24, 7 ], [ 5.25, 7 ], [ 5.26, 7 ], [ 5.27, 7 ], [ 5.279999999999999, 7 ], [ 5.28, 7 ], [ 5.29, 8 ], [ 5.3, 8 ], [ 5.31, 8 ], [ 5.319999999999999, 8 ], [ 5.32, 8 ], [ 5.33, 8 ], [ 5.34, 8 ], [ 5.35, 8 ] ] – Jorman Franzini Oct 16 '16 at 19:17