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?

7
  • can you share your code. Commented Oct 16, 2016 at 17:48
  • Is your input data always sorted on thr first element ? Commented Oct 16, 2016 at 17:56
  • It's not going to be easy, you're dealing with floating numbers, and you can't just add 0.01, and what happens if any numbers go above 5.99 does it then become 6.00. Seems like you should consider using a different format instead. Commented Oct 16, 2016 at 18:05
  • @Fahad I don't have yet the code, I don't know where to start! The 1st idea is to do it with a while loop, but I don't think is a good start Commented Oct 16, 2016 at 18:18
  • @adeneo The array is always sorted, and the main idea is to sort after any cycle Commented Oct 16, 2016 at 18:19

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

8 Comments

Good Idea, tnx. But, for performance purpose, I think that the @Redu aproach is better
@JormanFranzini, I made some updates to improve the performance. By my estimate, it should be about 3x faster than Redu's answer.
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
@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
Tnx for your reply, maybe I'm wrong, I use this repl.it/EAsK/0 to test your code, and this repl.it/EAsG/0 to test the Reud's one! Like I said, maybe I'm in the wrong way. Let me know
|
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);

4 Comments

Tnx @Redu work but if there's 2 value that is already consecutive, go on loop
@Jorman Franzini Hmm... You mean data like [[5.23,7],[5.28,7],[5.29,7],[5.32,8],[5.35,8]] Give me a sample data that fails and i will look into that.
Exactly, some time I've 2 or more value, this for example is some data [1.01,3],[1.04,4],[1.09,5],[1.10,6],[1.15,7]
Tnx, seems ok now! And is more fast than the loop aproach
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); 

1 Comment

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.