使用 JavaScript 在数组中查找最近的日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11795266/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 14:22:01  来源:igfitidea点击:

Find closest date in array with JavaScript

javascriptarrayssearch

提问by Rein

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

{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
  • 2012 年 8 月 2 日
  • 2012 年 8 月 4 日
  • 2012 年 8 月 23 日

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

我搜索 2012 年 8 月 11 日,我希望它返回 2012 年 8 月 4 日和 2012 年 8 月 23 日。

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.

但是,这将返回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.

奇怪的是,使用下面的代码,这仅适用于 10 个日期或更短的数组。每当我使用 11 个或更多日期的数组时,结果就会出乎意料。

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?

例如:使用 15 个日期的数组,从 2012 年 8 月 6 日开始,到 2012 年 8 月 21 日。如果我再打电话,findClosest(full_day_array, new Date("30/07/2012");您会期望它返回{nextIndex: 0, prevIndex: -1}. 但是,它返回{nextIndex: 7, prevIndex: -1}. 为什么?

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

采纳答案by Rein

This works, no matter how long the array of dates is:

无论日期数组有多长,这都有效:

function newFindClosest(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);
        // 3600 * 24 * 1000 = calculating milliseconds to days, for clarity.
        var diff = (arrDate - testDate) / (3600 * 24 * 1000);
        if(diff > 0) {
            before.push({diff: diff, index: i});
        } else {
            after.push({diff: diff, index: i});
        }
    }
    before.sort(function(a, b) {
        if(a.diff < b.diff) {
            return -1;
        }
        if(a.diff > b.diff) {
            return 1;
        }
        return 0;
    });

    after.sort(function(a, b) {
        if(a.diff > b.diff) {
            return -1;
        }
        if(a.diff < b.diff) {
            return 1;
        }
        return 0;
    });
    return {datesBefore: before, datesAfter: after};
}

回答by Bergi

You can easily use the sortfunctionwith a custom comparator function:

您可以轻松地将该sort函数与自定义比较器函数一起使用:

// 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 filterthe array for that:

要仅获得 之前或之后的结果diffdate,您可以为此过滤数组:

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

如果您有包含{the_date_object: new Date(...)}对象的自定义数组,则需要使用以下内容调整排序算法

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

回答by Zeta

If you use an array of Dateobjects instead of your self-defined structure it can be achieved very easily in O(N):

如果使用Date对象数组 而不是自定义结构,则可以在 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.

如果数组已排序,则可以通过二进制搜索在 O(log N) 中实现。

Edit: "it is crucial that I find both the closest match beforeand afterthe 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){
   // 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 */

回答by cantlin

Zeta'sanswer is excellent, but I was interested in how you'd approach this if you wanted to know the nearest N objects in either direction. Here's my stab:

Zeta 的回答非常好,但是如果您想知道任一方向上最近的 N 个对象,我对如何处理这个问题很感兴趣。这是我的刺:

var objects = [
    { 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 testDate = new Date('08/11/2012'),
    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]; });

console.log(['closest future date', objects[nextDateIndexesByDiff[0][0]]]);
console.log(['closest past date', objects[prevDateIndexesByDiff[0][0]]]);

回答by Philippe Genois

Here's what we use:

这是我们使用的:

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

此函数在数组中查找日期(名为dateParam)与dateToCompare最接近的项目。

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

对于每个项目[ dateParam],返回具有最接近 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

这个解决方案需要momentJS库