如何 GroupBy 时间范围(关闭时间分组在一起)
How to GroupBy time-range (closed-times grouped together)
假设我们有这样的记录:
NID CId PushedAt
120 796 2015-09-04 18:00:53.6012627 +00:00
120 967 2015-09-04 18:00:51.9891748 +00:00
119 669 2015-09-04 17:45:56.8179094 +00:00
119 955 2015-09-04 17:45:55.2078154 +00:00
119 100 2015-09-04 17:45:53.5867187 +00:00
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
113 109 2015-09-04 16:00:53.5269438 +00:00
113 111 2015-09-04 16:00:51.9168442 +00:00
107 603 2015-09-04 13:45:59.9994496 +00:00
我想按时间范围(不是特定时间)对它们进行分组。如果我按时间分组:
var grouped = list.GroupBy(t => new {
t.PushedAt.Year,
t.PushedAt.Month,
t.PushedAt.Day,
t.PushedAt.Hour,
t.PushedAt.Minute
});
然后我会想念具有不同 Minute
但实际上在同一组中的组。例如,这些行:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
将前往这些群组:
// group 1:
116 384 2015-09-04 17:01:01.5375630 +00:00
// group 2:
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
但是,我要找的是这个组:
// group 1:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
意味着,这 3 行应该组合在一起。比如说,所有在 5 分钟范围内的行都应该组合在一起。完整的输出应该是这样的:
// group 1:
120 796 2015-09-04 18:00:53.6012627 +00:00
120 967 2015-09-04 18:00:51.9891748 +00:00
// group 2:
119 669 2015-09-04 17:45:56.8179094 +00:00
119 955 2015-09-04 17:45:55.2078154 +00:00
119 100 2015-09-04 17:45:53.5867187 +00:00
// group 3:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
// group 4:
113 109 2015-09-04 16:00:53.5269438 +00:00
113 111 2015-09-04 16:00:51.9168442 +00:00
// group 5:
107 603 2015-09-04 13:45:59.9994496 +00:00
你有什么想法吗?
注意: NID
字段不可分组。
更新:
我知道我可以通过迭代项目来解决问题(正如 juharr 在评论中所说)。但是,我正在寻找 LINQ
解决方案(如果有的话)。谢谢。
根据 pasty
的评论,我想出了我可以将 IEqualityComparer<>
传递给 GroupBy
的方法。所以,我这样做了:
var grouped = list.GroupBy(t => t.PushedAt, new MyComparer());
有了这个比较器:
internal class MyComparer : IEqualityComparer<DateTime> {
private static readonly TimeSpan Span = TimeSpan.FromMinutes(5);
public bool Equals(DateTime x, DateTime y){
return (x - y).Duration() <= Span;
}
public int GetHashCode(DateTime obj) {
return obj.Year.GetHashCode() ^ obj.Month.GetHashCode() ^ obj.Day.GetHashCode();
}
}
这正是我要找的东西。
我认为这样的事情可能会有所帮助:
var list = new List<myClass>();
list.Add(new myClass(120, 796, new DateTime(2015, 09, 04, 18, 00, 53)));
list.Add(new myClass(120, 967, new DateTime(2015, 09, 04, 18, 03, 51)));
list.Add(new myClass(119, 669, new DateTime(2015, 09, 04, 17, 45, 56)));
list.Add(new myClass(119, 955, new DateTime(2015, 09, 04, 17, 42, 55)));
list.Add(new myClass(119, 100, new DateTime(2015, 09, 04, 17, 41, 53)));
list.Add(new myClass(116, 384, new DateTime(2015, 09, 04, 17, 01, 01)));
list.Add(new myClass(116, 155, new DateTime(2015, 09, 04, 17, 00, 59)));
list.Add(new myClass(116, 517, new DateTime(2015, 09, 04, 17, 00, 58)));
list.Add(new myClass(113, 109, new DateTime(2015, 09, 04, 16, 02, 53)));
list.Add(new myClass(113, 111, new DateTime(2015, 09, 04, 16, 00, 51)));
list.Add(new myClass(107, 603, new DateTime(2015, 09, 04, 13, 45, 59)));
var grouped = list.GroupBy(t =>
t.PushedAt.ToString("yyyyMMddHH") +
((int)(t.PushedAt.Minute / 5)).ToString("00")
);
foreach (var g in grouped) {
Console.WriteLine(g.Key);
foreach (var itm in g) {
Console.WriteLine(String.Format("{0}\t{1}\t{2}", itm.CId, itm.NID, itm.PushedAt));
}
}
控制台结果:
201509041800
796 120 9/4/2015 6:00:53 PM
967 120 9/4/2015 6:03:51 PM
201509041709
669 119 9/4/2015 5:45:56 PM
201509041708
955 119 9/4/2015 5:42:55 PM
100 119 9/4/2015 5:41:53 PM
201509041700
384 116 9/4/2015 5:01:01 PM
155 116 9/4/2015 5:00:59 PM
517 116 9/4/2015 5:00:58 PM
201509041600
109 113 9/4/2015 4:02:53 PM
111 113 9/4/2015 4:00:51 PM
201509041309
603 107 9/4/2015 1:45:59 PM
使用 .Ticks
属性.
这真的很容易做到
如果您从问题的输入开始:
var records = new[]
{
new { NID = 120, PID = 796, PushedAt = DateTime.Parse("2015-09-04 18:00:53.6012627") },
new { NID = 120, PID = 967, PushedAt = DateTime.Parse("2015-09-04 18:00:51.9891748") },
new { NID = 119, PID = 669, PushedAt = DateTime.Parse("2015-09-04 17:45:56.8179094") },
new { NID = 119, PID = 955, PushedAt = DateTime.Parse("2015-09-04 17:45:55.2078154") },
new { NID = 119, PID = 100, PushedAt = DateTime.Parse("2015-09-04 17:45:53.5867187") },
new { NID = 116, PID = 384, PushedAt = DateTime.Parse("2015-09-04 17:01:01.5375630") },
new { NID = 116, PID = 155, PushedAt = DateTime.Parse("2015-09-04 17:00:59.9284665") },
new { NID = 116, PID = 517, PushedAt = DateTime.Parse("2015-09-04 17:00:58.3193725") },
new { NID = 113, PID = 109, PushedAt = DateTime.Parse("2015-09-04 16:00:53.5269438") },
new { NID = 113, PID = 111, PushedAt = DateTime.Parse("2015-09-04 16:00:51.9168442") },
new { NID = 107, PID = 603, PushedAt = DateTime.Parse("2015-09-04 13:45:59.9994496") },
};
那么分组的方法如下:
var results =
records
.GroupBy(x => x.PushedAt.Ticks / TimeSpan.TicksPerMinute / 5);
我得到这些结果:
假设我们有这样的记录:
NID CId PushedAt
120 796 2015-09-04 18:00:53.6012627 +00:00
120 967 2015-09-04 18:00:51.9891748 +00:00
119 669 2015-09-04 17:45:56.8179094 +00:00
119 955 2015-09-04 17:45:55.2078154 +00:00
119 100 2015-09-04 17:45:53.5867187 +00:00
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
113 109 2015-09-04 16:00:53.5269438 +00:00
113 111 2015-09-04 16:00:51.9168442 +00:00
107 603 2015-09-04 13:45:59.9994496 +00:00
我想按时间范围(不是特定时间)对它们进行分组。如果我按时间分组:
var grouped = list.GroupBy(t => new {
t.PushedAt.Year,
t.PushedAt.Month,
t.PushedAt.Day,
t.PushedAt.Hour,
t.PushedAt.Minute
});
然后我会想念具有不同 Minute
但实际上在同一组中的组。例如,这些行:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
将前往这些群组:
// group 1:
116 384 2015-09-04 17:01:01.5375630 +00:00
// group 2:
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
但是,我要找的是这个组:
// group 1:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
意味着,这 3 行应该组合在一起。比如说,所有在 5 分钟范围内的行都应该组合在一起。完整的输出应该是这样的:
// group 1:
120 796 2015-09-04 18:00:53.6012627 +00:00
120 967 2015-09-04 18:00:51.9891748 +00:00
// group 2:
119 669 2015-09-04 17:45:56.8179094 +00:00
119 955 2015-09-04 17:45:55.2078154 +00:00
119 100 2015-09-04 17:45:53.5867187 +00:00
// group 3:
116 384 2015-09-04 17:01:01.5375630 +00:00
116 155 2015-09-04 17:00:59.9284665 +00:00
116 517 2015-09-04 17:00:58.3193725 +00:00
// group 4:
113 109 2015-09-04 16:00:53.5269438 +00:00
113 111 2015-09-04 16:00:51.9168442 +00:00
// group 5:
107 603 2015-09-04 13:45:59.9994496 +00:00
你有什么想法吗?
注意: NID
字段不可分组。
更新:
我知道我可以通过迭代项目来解决问题(正如 juharr 在评论中所说)。但是,我正在寻找 LINQ
解决方案(如果有的话)。谢谢。
根据 pasty
的评论,我想出了我可以将 IEqualityComparer<>
传递给 GroupBy
的方法。所以,我这样做了:
var grouped = list.GroupBy(t => t.PushedAt, new MyComparer());
有了这个比较器:
internal class MyComparer : IEqualityComparer<DateTime> {
private static readonly TimeSpan Span = TimeSpan.FromMinutes(5);
public bool Equals(DateTime x, DateTime y){
return (x - y).Duration() <= Span;
}
public int GetHashCode(DateTime obj) {
return obj.Year.GetHashCode() ^ obj.Month.GetHashCode() ^ obj.Day.GetHashCode();
}
}
这正是我要找的东西。
我认为这样的事情可能会有所帮助:
var list = new List<myClass>();
list.Add(new myClass(120, 796, new DateTime(2015, 09, 04, 18, 00, 53)));
list.Add(new myClass(120, 967, new DateTime(2015, 09, 04, 18, 03, 51)));
list.Add(new myClass(119, 669, new DateTime(2015, 09, 04, 17, 45, 56)));
list.Add(new myClass(119, 955, new DateTime(2015, 09, 04, 17, 42, 55)));
list.Add(new myClass(119, 100, new DateTime(2015, 09, 04, 17, 41, 53)));
list.Add(new myClass(116, 384, new DateTime(2015, 09, 04, 17, 01, 01)));
list.Add(new myClass(116, 155, new DateTime(2015, 09, 04, 17, 00, 59)));
list.Add(new myClass(116, 517, new DateTime(2015, 09, 04, 17, 00, 58)));
list.Add(new myClass(113, 109, new DateTime(2015, 09, 04, 16, 02, 53)));
list.Add(new myClass(113, 111, new DateTime(2015, 09, 04, 16, 00, 51)));
list.Add(new myClass(107, 603, new DateTime(2015, 09, 04, 13, 45, 59)));
var grouped = list.GroupBy(t =>
t.PushedAt.ToString("yyyyMMddHH") +
((int)(t.PushedAt.Minute / 5)).ToString("00")
);
foreach (var g in grouped) {
Console.WriteLine(g.Key);
foreach (var itm in g) {
Console.WriteLine(String.Format("{0}\t{1}\t{2}", itm.CId, itm.NID, itm.PushedAt));
}
}
控制台结果:
201509041800
796 120 9/4/2015 6:00:53 PM
967 120 9/4/2015 6:03:51 PM
201509041709
669 119 9/4/2015 5:45:56 PM
201509041708
955 119 9/4/2015 5:42:55 PM
100 119 9/4/2015 5:41:53 PM
201509041700
384 116 9/4/2015 5:01:01 PM
155 116 9/4/2015 5:00:59 PM
517 116 9/4/2015 5:00:58 PM
201509041600
109 113 9/4/2015 4:02:53 PM
111 113 9/4/2015 4:00:51 PM
201509041309
603 107 9/4/2015 1:45:59 PM
使用 .Ticks
属性.
如果您从问题的输入开始:
var records = new[]
{
new { NID = 120, PID = 796, PushedAt = DateTime.Parse("2015-09-04 18:00:53.6012627") },
new { NID = 120, PID = 967, PushedAt = DateTime.Parse("2015-09-04 18:00:51.9891748") },
new { NID = 119, PID = 669, PushedAt = DateTime.Parse("2015-09-04 17:45:56.8179094") },
new { NID = 119, PID = 955, PushedAt = DateTime.Parse("2015-09-04 17:45:55.2078154") },
new { NID = 119, PID = 100, PushedAt = DateTime.Parse("2015-09-04 17:45:53.5867187") },
new { NID = 116, PID = 384, PushedAt = DateTime.Parse("2015-09-04 17:01:01.5375630") },
new { NID = 116, PID = 155, PushedAt = DateTime.Parse("2015-09-04 17:00:59.9284665") },
new { NID = 116, PID = 517, PushedAt = DateTime.Parse("2015-09-04 17:00:58.3193725") },
new { NID = 113, PID = 109, PushedAt = DateTime.Parse("2015-09-04 16:00:53.5269438") },
new { NID = 113, PID = 111, PushedAt = DateTime.Parse("2015-09-04 16:00:51.9168442") },
new { NID = 107, PID = 603, PushedAt = DateTime.Parse("2015-09-04 13:45:59.9994496") },
};
那么分组的方法如下:
var results =
records
.GroupBy(x => x.PushedAt.Ticks / TimeSpan.TicksPerMinute / 5);
我得到这些结果: