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
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};
}
You can easily use the sort
function with a custom comparator function:
// assuming you have an array of Date objects - everything else is crap:
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; // sort a before b when the distance is smaller
});
// result:
[2012-08-05, 2012-08-04, 2012-08-01, 2013-03-20]
To get only results before or after the diffdate
, you can filter the array for that:
var beforedates = arr.filter(function(d) {
return d - diffdate < 0;
}),
afterdates = arr.filter(function(d) {
return d - diffdate > 0;
});
If you have your custom array with the {the_date_object: new Date(...)}
objects, you will need to adapt the sort algorithm with
var distancea = Math.abs(diffdate - a.the_date_object);
var distanceb = Math.abs(diffdate - b.the_date_object);
If you use an array of Date
objects instead of your self-defined structure it can be achieved very easily in O(N):
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 best date will be days[bestDate] */
If the array is sorted it can be achieved in O(log N) with binary search.
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){
// If currDiff is negative, then testDate is more in the past than days[i].
// This means, that from testDate's point of view, days[i] is in the future
// and thus by a candidate for the next date.
bestNextDate = i;
bestNextDiff = currDiff;
}
if(currDiff > 0 && currDiff < bestPrevDiff){
// If currDiff is positive, then testDate is more in the future than days[i].
// This means, that from testDate's point of view, days[i] is in the past
// and thus by a candidate for the previous date.
bestPrevDate = i;
bestPrevDiff = currDiff;
}
}
/* days[bestPrevDate] is the best previous date,
days[bestNextDate] is the best next date */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With