C# 如何从列表

C# 如何从列表,c#,linq,list,datetime,C#,Linq,List,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 LastYea

假设我有最近的日期时间和所有可能日期的列表。我如何才能有效地在列表中找到与去年日期最接近的日期时间

假设我的列表由以下内容组成:

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函数的哪个方向进行检查,我们将不胜感激。

只需根据列表中的日期和您要查找的日期之间的差排序即可:

var dateToFind = currentDate.AddYears(-1);
times.OrderBy(t => (t - dateToFind).Duration).FirstOrDefault();

两个日期之间的差异是TimeSpan的一个实例;Duration属性返回绝对值

仅按列表中的日期与您要查找的日期之间的差值排序:

var dateToFind = currentDate.AddYears(-1);
times.OrderBy(t => (t - dateToFind).Duration).FirstOrDefault();

两个日期之间的差异是TimeSpan的一个实例;Duration属性在排序时返回绝对值

,您可以使用二进制搜索来尝试查找精确匹配。如果返回一个非负数,您就知道找到了一个精确的匹配项。否则,您可以应用按位补码运算符来查找将插入值的索引。然后需要检查该索引之前或处的值是否离目标更远。比如说:

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;
话虽如此,斯宾德对托马斯·列夫斯基答案的评论给出了一个非常简单的解决方案:

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

请注意,这始终是非负的;它类似于Math.Abs,但对于时间跨度值。

当它被排序时,您可以使用二进制搜索来尝试找到精确匹配。如果返回一个非负数,您就知道找到了一个精确的匹配项。否则,您可以应用按位补码运算符来查找将插入值的索引。然后需要检查该索引之前或处的值是否离目标更远。比如说:

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;
话虽如此,斯宾德对托马斯·列夫斯基答案的评论给出了一个非常简单的解决方案:

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

请注意,这始终是非负的;这就像Math.Abs,但时间跨度值。

你说的去年的日期到底是什么意思?如果你能提供一个简短但完整的例子,那会有所帮助。我指的是我提供的日期时间,但要提前一年。我用一个例子更新了这个问题。列表已经排序了吗?您可以从最近的一个条目中返回52个条目。@mikez该列表与时间段不一致,可以是周、月、季度、年等。您所说的去年的日期到底是什么意思?如果你能提供一个简短但完整的例子,那会有所帮助。我指的是我提供的日期时间,但要提前一年。我用一个例子更新了这个问题。列表已经排序了吗?您可以从最近的一个条目中返回52个条目。@mikez列表与时间段不一致,可以是周、月、季度、年,等等。你不需要按绝对时间跨度订购吗?这只会找到列表中最早的日期,而不是最接近的日期。那么,如果最接近去年的日期比你提供的日期提前一天,会发生什么情况?例如,我有2014年3月21日和2014年3月26日。我现在的日期是2015年3月25日。这只会给我2014年3月21日,而不是2014年3月26日。@EthanDeLong order by Math.abs-dateToFind.TotalDaysinstead@spender,说得好!我修好了,谢谢。顺便说一句,有一种比Math更简单的方法。Abs:Timespan.Duration属性。您不需要按绝对Timespan排序吗?这只会在列表中找到最早的日期,而不是最接近的日期。那么,如果与去年最接近的日期比您提供的日期提前一天,会发生什么情况?例如,我有2014年3月21日和2014年3月26日。我现在的日期是2015年3月25日。这只会给我2014年3月21日,而不是2014年3月26日。@EthanDeLong order by Math.abs-dateToFind.TotalDaysinstead@spender,说得好!我修好了,谢谢。顺便说一句,有一种比Math.Abs更简单的方法:Timespan.Duration属性。