Determining the Nearest Date in an Array Using JavaScript

In my array, I store information about each day as an object. For example:

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

To enhance the day objects, I have included a timestamp attribute using the following function:

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 order of the days in the array is random without any specific logic.

Now, my goal is to identify the two closest dates to a given date. So, for instance, if my array contains:

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

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

I attempted to use a solution from another source which 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, I encountered issues as it returned unidentified.

What would be the most efficient way to achieve this while minimizing processor and memory usage?

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

I now generate an array of dates using the following approach:

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

Anomalies arise when working with arrays containing more than 10 dates. For example, with a range from August 6, 2012, to August 21, 2012 consisting of 15 dates. When applying

findClosest(full_day_array, new Date("30/07/2012"));
one might predict a result of {nextIndex: 0, prevIndex: -1}. Surprisingly, it returns {nextIndex: 7, prevIndex: -1}. Why does this discrepancy occur?

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

Answer №1

To utilize the sort function effectively, you can implement a customized comparator function:

// assuming you are working with an array of Date objects - everything else is irrelevant:
var arr = [new Date(2012, 7, 1), new Date(2012, 7, 4), new Date(2012, 7, 5), new Date(2013, 2, 20)];
var diffdate = new Date(2012, 7, 11);

arr.sort(function(a, b) {
    var distancea = Math.abs(diffdate - a);
    var distanceb = Math.abs(diffdate - b);
    return distancea - distanceb; // arrange a before b when the distance is shorter
});

// result:
[2012-08-05, 2012-08-04, 2012-08-01, 2013-03-20]

If you only want results prior to or after the diffdate, you can filter the array accordingly:

var beforedates = arr.filter(function(d) {
    return d - diffdate < 0;
}),
    afterdates = arr.filter(function(d) {
    return d - diffdate > 0;
});

In case your custom array contains objects like {the_date_object: new Date(...)}, you will need to modify the sort algorithm with

    var distancea = Math.abs(diffdate - a.the_date_object);
    var distanceb = Math.abs(diffdate - b.the_date_object);

Answer №2

Instead of using a self-defined structure, you can achieve the desired result easily in O(N) by utilizing an array of Date objects:

var testDate = new Date(...);
var bestDate = days.length;
var bestDiff = -(new Date(0,0,0)).valueOf();
var currDiff = 0;
var i;

for(i = 0; i < days.length; ++i){
   currDiff = Math.abs(days[i] - testDate);
   if(currDiff < bestDiff){
       bestDate = i;
       bestDiff = currDiff;
   }   
}

/* The closest match will be found at days[bestDate] */

If the array is sorted, achieving this in O(log N) through binary search becomes possible.

Edit: "it is crucial that I find both the closest match before and after the date"

var testDate = new Date(...);

var bestPrevDate = days.length;
var bestNextDate = days.length;

var max_date_value = Math.abs((new Date(0,0,0)).valueOf());

var bestPrevDiff = max_date_value;
var bestNextDiff = -max_date_value;

var currDiff = 0;
var i;

for(i = 0; i < days.length; ++i){
   currDiff = testDate - days[i].the_date_object;
   if(currDiff < 0 && currDiff > bestNextDiff){
       bestNextDate = i;
       bestNextDiff = currDiff;
   }
   if(currDiff > 0 && currDiff < bestPrevDiff){
       bestPrevDate = i;
       bestPrevDiff = currDiff;
   }   

}
/* days[bestPrevDate] represents the best previous date,
   days[bestNextDate] represents the best next date */

Answer №3

Give this a shot

let datesArray = [
'May 7, 2001 12:45:00',
'June 10, 2001 12:45:00',
'July 11, 2001 12:45:00',
'August 5, 2001 12:45:00',
'September 2, 2001 12:45:00',
'October 6, 2001 12:45:00',
]

let timeDifference = datesArray.map(date => Math.abs(new Date() - new Date(date).getTime()));
let shortestTimeIndex = timeDifference.indexOf(Math.min(...timeDifference));
console.log(datesArray[shortestTimeIndex]);

Answer №4

Omega's response is top-notch, but my curiosity lies in exploring the approach for finding the nearest N objects in both directions. Here is my take on it:

var items = [
    { day_year: "2012",
      day_month: "08",
      day_number: "02"
    },
    { day_year: "2012",
      day_month: "08",
      day_number: "04"
    },
    { day_year: "2012",
      day_month: "08",
      day_number: "23"
    }
];

var testingDate = new Date('08/11/2012'),
    nextIndexesByDifference = [],
    prevIndexesByDifference = [];

for(var j = 0; j < items.length; j++) {
    var currentDateStr = [items[j].day_month, items[j].day_number, items[j].day_year].join('/'),
        currentDate    = new Date(currentDateStr),
        currentDiff     = testingDate - currentDate;

    currentDiff < 0
        ? nextIndexesByDifference.push([j, currentDiff])
        : prevIndexesByDifference.push([j, currentDiff]);
}

nextIndexesByDifference.sort(function(x, y) { return x[1] < y[1]; });
prevIndexesByDifference.sort(function(x, y) { return x[1] > y[1]; });

console.log(['closest future date', items[nextIndexesByDifference[0][0]]]);
console.log(['closest past date', items[prevIndexesByDifference[0][0]]]);

Answer №5

Here's how we approach it:

In this specific function, we are targeting the closest date within an array to a given date parameter (dateToCompare).

By iterating through each item in the array and comparing their respective dates to dateToCompare, we aim to return the element with the date closest to the comparison date.

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

Note: This solution relies on the momentJS library for accurate date comparisons.

Answer №6

This function efficiently finds the closest date in an array regardless of its length:

function efficientDateFinder(dates, testDate) {
    var before = [];
    var after = [];
    var max = dates.length;
    for(var i = 0; i < max; i++) {
        var tar = dates[i];
        var arrDate = new Date(tar.day_year, tar.day_month, tar.day_number);
        var diff = (arrDate - testDate) / (3600 * 24 * 1000); // Convert milliseconds to days.
        if(diff > 0) {
            before.push({diff: diff, index: i});
        } else {
            after.push({diff: diff, index: i});
        }
    }
    before.sort(function(a, b) {
        return a.diff - b.diff;
    });

    after.sort(function(a, b) {
        return b.diff - a.diff;
    });
    return {datesBefore: before, datesAfter: after};
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The sidebar is not correctly displaying at full height

Having trouble getting the sidebar to fit perfectly between the header/nav and footer in full height. <html> <head> <meta charset="utf-8"> </head> <body> <header> <div class="header"> & ...

Determine whether a response is not received within 8 seconds

One of the methods in my Angular component is responsible for returning data Here is a snippet of that method getRecognitionById() { this.loaderService.show(null, true); forkJoin( this.vendorWebApiService.getRecognitionById(this.executiveCh ...

Choose an option removed upon clicking

I need to remove the option with a value of 0 when the user selects from the dropdown list. Choose: <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <form:select id="CONTEXTE" path="CONTEXTE" onclick="go()" class="s ...

extract the information from the JSON structure

Currently, I am in the process of learning JSON. $.ajax({ async: true, type: "POST", url: "fetch.....data.jsp", data: "vendorId="+vendor, success: function(json){ alert( "Received Data: " + ...

Maintain the dropdown selection while the table is being updated

My table updates every few seconds and each row has a dropdown menu that allows users to take actions on that row. However, the problem is that when new data is received from the database, the dropdown menu closes if it was open. This can be frustrating f ...

Preventing document.getElementById from throwing errors when the element is null

Is there a way to handle null values returned by document.getElementById in JavaScript without adding if statements or checks to the code? I'm looking for a solution that allows the execution of subsequent JavaScript calls even after encountering a nu ...

Creating an expandable discussion area (part II)

After checking out this query that was posted earlier, I am interested in implementing a similar feature using AJAX to load the comment box without having to refresh the entire page. My platform of choice is Google App Engine with Python as the primary lan ...

Developing a Secondary User within Meteor.JS after Establishing the Primary User

Is it possible to automatically create a secondary user upon registration of the primary user using a form generated with the useraccounts:core package? An issue arises when attempting to run Accounts.createUser within Accounts.onCreateUser, resulting in ...

Aligning validation schema with file type for synchronization

Below is the code snippet in question: type FormValues = { files: File[]; notify: string[]; }; const validationSchema = yup.object({ files: yup .array<File[]>() .of( yup .mixed<File>() .required() .t ...

What is the best way to activate a function from a modal component without having the function embedded in Angular 14?

I've recently been putting together an e-commerce application using Angular 14. Currently, I'm tackling a form that must only be submitted once the user accepts the "Terms & Conditions" shown in a modal popup. The FormComponent and ModalCompone ...

How can we activate navigation on a mobile browser?

I am currently working on a small HTML5/JavaScript website designed to be accessed by mobile browsers on devices such as Android and iPhone. I am utilizing the geolocation feature of HTML5 to obtain the user's current location, but my goal is to then ...

How about a custom-designed talking JavaScript pop-up?

Looking to customize the appearance (CSS, buttons...) of a "confirm" JavaScript dialog box while ensuring it remains persistent. The dialog box appears after a countdown, so it is crucial that it maintains focus even if the user is on another browser tab o ...

When the button is clicked, I would like to use JavaScript to toggle the visibility of a div, allowing it to open and

I am attempting to toggle a div using JavaScript when the open and close button is clicked. However, I have encountered an issue where the div disappears when the button is clicked, possibly due to post-back. var toggle = function () { var mydiv = d ...

Alternative to updating object coordinates in Fabric JS 1.7.9 - seeking solutions for migration challenges

Update: JSFiddle: https://jsfiddle.net/Qanary/915fg6ka/ I am currently working on implementing the `curveText` function (found at the bottom of this post). It was functioning properly with `fabric.js 1.2.0`, but after updating to `fabric.js 1.7.9`, I not ...

What sets class/instance methods apart from static methods when it comes to their functionality in applications?

As I develop APIs for my application, I've been curious about the difference between defining functionality methods like this: class Foo { static method1(req, res) {} static method2(req, res) {} } and class Foo { method1(req, res) {} method ...

Automating Image Downloads with Puppeteer by Adding Authentication Query String to Image URL

Attempting to save images stored in a web-space account can be challenging. Accessing the private space with credentials and retrieving the image link using Puppeteer works smoothly. However, when the src attribute of the image includes additional authenti ...

Cross-Origin Resource Sharing (CORS) verification for WebSocket connections

I am currently utilizing expressjs and have implemented cors validation to allow all origins. const options = { origin: ['*'], credentials: true, exposedHeaders: false, preflightContinue: false, optionsSuccessStatus: 204, methods: [&a ...

The search filter in react material-table is not functioning properly upon initialization

Search functionality is functioning correctly in this code snippet: <MaterialTable columns={[ { title: 'Name', field: 'firstname', type: 'string' } ]} /> Unfortunately, the Search filte ...

Obtain data from a struct within a Swift file

Recently, I created a UserAccount class in my Swift file with various properties like userName, email, instaGram, and more. Here's a snippet of how the class looks: class UserAccount { var userName: String! var email: String! var instaGram: Strin ...

The Tab component's onClick event is nonfunctional

I am currently utilizing the Tab feature from the material-ui library in my React project. As I try to come up with a workaround for an issue I am facing, I notice that my onClick event listener is not being triggered. An example of one of the tabs: < ...