假设我有最新的DateTime和所有可能日期的列表。我如何才能有效地找到列表中距离去年最近的约会时间?
说我的清单由以下内容组成:
2014-03-07
2014-03-14
2014-03-21
2014-03-28
...
2015-03-06
2015-03-13
2015-03-20
我最近的约会是 2015-03-20 ,但我想检索去年的日期, 2014-03-21 。
这就是我目前所拥有的,但如果去年的某一天休息一天(例如,我的时间段每周存储一次),它就无法工作。
public DateTime LastYearDate()
{
List<DateTime> times = GetAllDates();
times.Sort();
times.Reverse();
DateTime currentDate = times.First();
return times.Where(dt => dt == currentDate.AddYears(-1)).First();
}
我不确定我会用什么来递归计算最接近的日期,所以如果您对我应该采取的方向有任何想法(参考任何Linq函数来检查),那将不胜感激。< / p>
答案 0 :(得分:7)
只需按照列表中的日期与您要查找的日期之间的差异进行排序:
var dateToFind = currentDate.AddYears(-1);
times.OrderBy(t => (t - dateToFind).Duration).FirstOrDefault();
(两个日期之间的差异是TimeSpan
的实例; Duration
属性返回绝对值)
答案 1 :(得分:4)
在排序后,您可以使用二进制搜索来尝试查找完全匹配。如果List<T>.BinarySearch
返回非负数,则表示您已找到完全匹配。否则,您可以应用按位补码运算符来查找将插入值的索引。然后,您需要检查该索引之前或之后的值是否远离目标。所以像这样:
var target = currentDate.AddYears(-1);
List<DateTime> times = GetAllDates();
if (times.Count == 0)
{
// ??? Work out what you want to do here, e.g. throw an exception
}
times.Sort();
var index = times.BinarySearch(target);
if (index >= 0)
{
return times[index];
}
int insertIndex = ~index;
// Handle boundary cases
if (insertIndex == 0)
{
return times[0];
}
if (insertIndex == times.Count)
{
return times[insertIndex - 1];
}
// Okay, two options - find the closest
var timeBefore = times[insertIndex - 1];
var timeAfter = times[insertIndex];
// TODO: Work out what you want to do if they're equidistant.
return target - timeBefore > timeAfter - target ? timeAfter : timeBefore;
话虽如此,Spender对Thomas Levesque的回答给出了一个非常简单的解决方案:
var target = currentDate.AddYears(-1);
List<DateTime> times = GetAllDates();
if (times.Count == 0)
{
// ??? Work out what you want to do here, e.g. throw an exception
}
return times.OrderBy(t => (target - t).Duration).First();
请注意,TimeSpan.Duration
始终为非负数;它与Math.Abs
相似,但适用于TimeSpan
值。