我有一个DateTime项目列表,我想删除彼此在2分钟内的任何内容(遇到的第一个应该保留)。有人能告诉我如何使用LINQ实现这一目标吗?是否需要扩展方法?
澄清一些样本数据:
00:00:00 00:01:30 00:02:30 00:05:00
应该返回:
00:00:00 00:05:00
答案 0 :(得分:3)
所以这里的想法是先将项目分组。在浏览列表中的项目(已排序)时,如果当前项目在前一项目的阈值内,它应该进入同一组,如果没有,它应该开始自己的组。
我们可以创建一个GroupWhile
函数,该函数接受一个给出前一个和当前项的函数,并确定它们是否应该组合在一起。我们对给定条件的数据,组进行排序,然后取每组中的第一项。
public static IEnumerable<DateTime> LoneDates(
IEnumerable<DateTime> dates, TimeSpan threshold)
{
return dates.OrderBy(x => x)
.GroupWhile((previous, current) => current - previous <= threshold)
.Select(group => group.First());
}
至于实施GroupWhile
,可以这样做:
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> list = new List<T>() { iterator.Current };
T previous = iterator.Current;
while (iterator.MoveNext())
{
if (predicate(previous, iterator.Current))
{
list.Add(iterator.Current);
}
else
{
yield return list;
list = new List<T>() { iterator.Current };
}
previous = iterator.Current;
}
yield return list;
}
}
通过阅读它将第一个项目放在它自己的组中,然后它遍历序列中的每个其他项目;如果给定的函数说它应该被添加到当前组,如果没有,则将当前组发送到输出序列并创建一个新组。
使用您的示例输入:
var data = new List<DateTime>()
{
DateTime.Today,
DateTime.Today.AddMinutes(1.5),
DateTime.Today.AddMinutes(2.5),
DateTime.Today.AddMinutes(5),
};
var query = LoneDates(data, TimeSpan.FromMinutes(2));
Console.WriteLine(string.Join("\n", query));
结果:
2013年8月30日12:00:00
8/30/2013 12:05:00 AM
这是预期的输出。
答案 1 :(得分:0)
当且仅当没有前一个小于2秒的时间时,此查询才会给你一个时间T.
IEnumerable<DateTime> times = ...;
var query = times
.OrderBy(x => x)
.Throttle((x, y) => y.Subtract(x) <= TimeSpan.FromSeconds(2));
public static IEnumerable<T> Throttle(
this IEnumerable<T> source, Func<T, T, bool> collapse)
{
var first = true;
var prev = default(T);
foreach (var curr in source)
{
if (first || !collapse(prev, curr))
{
yield return curr;
first = false;
}
prev = curr;
}
}
答案 2 :(得分:0)
private List<DateTime> RemoveItems(List<DateTime> times)
{
var newtimes = new List<DateTime>();
var previoustime = new DateTime();
var firsttime = times[0];
newtimes.Add(firsttime);
foreach (var time in times)
{
if (firsttime == time)
{
previoustime = time;
continue;
}
if ((time - previoustime) > new TimeSpan(0,0,1,30))
{
newtimes.Add(time);
}
previoustime = time;
}
return newtimes;
}
答案 3 :(得分:0)
这是我测试的另一个解决方案,似乎有效:
//build the data to test
List<DateTime> data = new List<DateTime>();
Random rand = new Random();
for (int i = 0; i < 50; i++) {
data.Add(new DateTime(2013, 12, 22, 12, rand.Next(50),0));
}
//----------
DateTime fix = DateTime.Now;
int j = 0;
var result = data.OrderBy(x => x)
.Select((x,i)=>new{x,i})
.GroupBy(x=> {
if(x.i == 0) fix = x.x;
else if ((x.x - fix).TotalMinutes >= 2)
{
fix = x.x;
j++;
}
return j;
}, e=>e.x, (key,e)=>e.First());