David
David

Reputation: 23

Finding the closest time from an array

I have an object array which looks like this

var OGArray = [
 { time: '16:33:53', import_today: '1.10' },
 { time: '16:28:20', import_today: '1.10' },
 { time: '16:23:07', import_today: '1.10' }]

The time automatically updates from an API. I also have an array that looks like this

var timeArray = ['16:00:00', '16:30:00', '17:00:00', '17:30:00']

I want to be able to look at the timeArray[0] and find the closest time match from OGArray.time and then pull what the import_today is

Upvotes: 1

Views: 893

Answers (2)

RobG
RobG

Reputation: 147343

You can use a small helper to convert time to a common base, say seconds, then compare the absolute differences between the times in the two arrays. Array.prototype.reduce can help to get the one with the lowest difference.

I don't know how you intend to call this or what actual result you want, but here's one way to get the closest object in OGArray for each element in timeArray. Once you have the closest object, you can do what you want with it.

It's a little more efficient than using a Date object, but loops through every element in OGArray for each element in timeArray. If the elements are sorted, then it can be optimised a little by using a loop instead of reduce and breaking as soon as diff starts to increase.

If OGArray is really large (say more than 1,000 elements) and sorted by time, you might use a binary search to find the closest element. That way you do no more than 10 to 20 searches even if the array is 1,000,000 elements or so.

function timeToSecs(time) {
  let [h, m, s] = time.split(':');
  return h*3.6e3 + m*60 + s*1;
}

var OGArray = [
  { time: '16:33:53', import_today: '1.10' },
  { time: '16:28:20', import_today: '1.10' },
  { time: '16:23:07', import_today: '1.10' }
];

var timeArray = ['16:00:00', '16:30:00', '17:00:00', '17:30:00']

// For each element in timeArray
timeArray.forEach(time => {
  let secs = timeToSecs(time);
  let closest = null;
  // Find closest element in OGArray
  OGArray.reduce((acc, obj, i) => {
    let diff = Math.abs(timeToSecs(obj.time) - secs);
    if (diff < acc) {
      acc = diff;
      closest = obj;
    }
    return acc;
  }, Number.POSITIVE_INFINITY);
  // Display result
  console.log('Time   : ' + time + 
            '\nClosest: ' + JSON.stringify(closest)); 
});

Upvotes: 1

epascarello
epascarello

Reputation: 207501

It is just a loop over and you do the calculation to what time is closer and you store it. Pick the one that is less.

Below is not the most efficient code, but it works.

var OGArray = [
 { time: '16:33:53', import_today: '1.10' },
 { time: '16:28:20', import_today: '1.10' },
 { time: '16:23:07', import_today: '1.10' }]

var timeArray = ['16:00:00', '16:30:00', '17:00:00', '17:30:00']

var closestArray = timeArray.map(t => {
  const ts = new Date(`01/01/2022 ${t}`);
  return OGArray.reduce((closest, item) => {
    const dC = Math.abs(new Date(`01/01/2022 ${closest.time}`) - ts);
    const dI = Math.abs(new Date(`01/01/2022 ${item.time}`) - ts);
    return dC < dI ? closest : item;
  });
})

console.log(closestArray);

Upvotes: 1

Related Questions