是否有方法根据它们之间的差异对数据元素进行分组?
List<Message> messages = new List<Message>();
class Message
{
private DateTime Date { get; set; }
private string Text { get; set; }
}
消息日期中的示例数据:
14.3 2014 20:28:15
14.3 2014 20:32:17
14.3 2014 20:37:25
14.3 2014 22:38:43
14.3 2014 20:40:23
14.3 2014 20:42:07
14.3 2014 20:43:54
14.3 2014 20:52:26
14.3 2014 20:53:41
14.3 2014 20:55:37
14.3 2014 20:58:44
我需要这样一个LINQ
查询组。如果6分钟内没有记录,将开始一个新的分组。
Group 1
14.3 2014 20:28:15
14.3 2014 20:32:17
Group 2
14.3 2014 20:37:25
14.3 2014 20:38:43
14.3 2014 20:40:23
14.3 2014 20:42:07
14.3 2014 20:43:54
Group 3
14.3 2014 20:52:26
14.3 2014 20:53:41
14.3 2014 20:55:37
14.3 2014 20:58:44
当然,我们可以创建自己的GroupWhile
方法,它允许我们在满足条件时对项目进行分组:
var query = messages.GroupWhile((prev, current) =>
prev.Date.AddMinutes(6) >= current.Date));
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))
{
yield return list;
list = new List<T>();
}
list.Add(iterator.Current);
previous = iterator.Current;
}
yield return list;
}
}
如果你想只使用本地Linq方法做到这一点,你必须在你的Linq查询之外保持一种状态变量。这不是一个好的做法,但这是可行的:
var groupNum = 0;
var prevDate = DateTime.MinValue;
var threshold = -TimeSpan.FromMinutes(6); // negative time makes things easier
var groups = messages.GroupBy(
m => prevDate - (prevDate = m.Date) < threshold ? ++groupNum : groupNum);
同样,在Linq查询之外跟踪状态变量是不推荐的,这种代码非常丑陋和令人困惑。
我可能会在生产环境中使用更像Servy的的答案。