Find closest date in array with JavaScript

Viewed 35421

I have an array with days in it. Each day is an object, for example:

{day_year: "2012", day_month: "08", day_number: "03", day_name: "mon"}

I have also added a timestamp attribute to each day object, by using:

function convertDays() {
    var max_i = days.length;
    for(var i = 0; i < max_i; i++) {
        var tar_i = days[i];
        tar_i.timestamp = new Date(tar_i.day_year, tar_i.day_month, tar_i.day_number);
    }
}

The days in the array are arbitrary, so there is no real logic to them.

Now I want to find the two closest days to any given date. So if the array with days contains

  • August 2, 2012
  • August 4, 2012
  • August 23, 2012

And I search for August 11, 2012, I want it to return August 4, 2012 and August 23, 2012.

I have tried using an answer from another question, that looks like this:

function findClosest(a, x) {
    var lo, hi;
    for(var i = a.length; i--;) {
        if(a[i] <= x && (lo === undefined || lo < a[i])) lo = a[i];
        if(a[i] >= x && (hi === undefined || hi > a[i])) hi = a[i];
    }
    return [lo, hi];
}

However, this returns unidentified.

What would be the most efficient (least processor/memory intensive way) to achieve this?

Edit: "However, how are those results "strange"? Could you provide an example of your code and data?"

I'm now using the following to generate an array of dates:

var full_day_array = [];
for(var i = 0; i < 10; i++) {
    var d = new Date();
    d.setDate(d.getDate() + i);
    full_day_array.push({day_year: d.getFullYear().toString(), day_month: (d.getMonth() + 1).toString(), day_number: d.getDate().toString()});
}

The strange part is, using the code below, this only works for an array of 10 dates or shorter. Whenever I use an array of 11 or more dates, the results become unexpected.

For instance: using an array of 15 dates, starting on August 6, 2012, to August 21, 2012. If I then call findClosest(full_day_array, new Date("30/07/2012"); you would expect it to return {nextIndex: 0, prevIndex: -1}. However, it returns {nextIndex: 7, prevIndex: -1}. Why?

function findClosest(objects, testDate) {
    var nextDateIndexesByDiff = [],
        prevDateIndexesByDiff = [];

    for(var i = 0; i < objects.length; i++) {
        var thisDateStr = [objects[i].day_month, objects[i].day_number, objects[i].day_year].join('/'),
            thisDate    = new Date(thisDateStr),
            curDiff     = testDate - thisDate;

        curDiff < 0
            ? nextDateIndexesByDiff.push([i, curDiff])
            : prevDateIndexesByDiff.push([i, curDiff]);
    }

    nextDateIndexesByDiff.sort(function(a, b) { return a[1] < b[1]; });
    prevDateIndexesByDiff.sort(function(a, b) { return a[1] > b[1]; });


    var nextIndex;
    var prevIndex;

    if(nextDateIndexesByDiff.length < 1) {
        nextIndex = -1;
    } else {
        nextIndex = nextDateIndexesByDiff[0][0];
    }
    if(prevDateIndexesByDiff.length < 1) {
        prevIndex = -1;
    } else {    
        prevIndex = prevDateIndexesByDiff[0][0];
    }
    return {nextIndex: nextIndex, prevIndex: prevIndex};
}
6 Answers

You Can try it like this

var dates = [
'July 16, 1995 03:24:00',
'Aug 18, 1995 03:24:00',
'August 19, 1995 03:24:00',
'September 17, 1995 03:24:00',
'September 14, 1995 03:24:00',
'August 18, 1995 03:24:00',
'July 16, 1995 03:24:00',
'December 15, 1995 03:24:00',
'July 13, 1995 03:24:00',
]

var temp = dates.map(d => Math.abs(new Date() - new Date(d).getTime()));
var idx = temp.indexOf(Math.min(...temp));
console.log(dates[idx]);

Here's what we use:

This function looks in array for the item which has the closest date (named dateParam) to dateToCompare.

For each item[dateParam], returns array's element which has the closest date to dateToCompare

getClosestDateInArray (array, dateParam, dateToCompare) {
  let minDiff = null;
  let mostAccurateDate = array[0];
  array.map((item) => {
    const diff = Math.abs(moment(dateToCompare).diff(item[dateParam], 'minutes', true));
    if (!minDiff || diff < minDiff) {
      minDiff = diff;
      mostAccurateDate = item
    }
  });
  return mostAccurateDate;
}

This solution require momentJS library

Related