[英]GroupBy DateTime column with offset C# Linq
在我的.NET 4.5.2 C#控制台應用程序中,我有一個名為someItems
的List<SomeItem>
其中SomeItem
為:
public class SomeItem {
public int A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
}
然后我將這些項目分組到基於A
和B
的新列表中:
var groupedItems = someItems.GroupBy(x => new { x.A, x.B });
我現在希望將C
引入GroupBy
,但是有一個問題:我想將其C
屬性SomeItems
2秒的( - 或者加或減)的SomeItems
組合在一起(基本上是2秒的偏移量)。 因此,例如,兩個SomeItem
,一個C
設置為2016-03-28 17:58:01.000
,另一個設置為2016-03-28 17:58:03.000
,將組合在一起。 那可能嗎?
編輯:對於所有意圖和目的,讓我們假設列表中沒有可能導致“重疊”組的項目(如評論中的@J.Sten的問題)
編輯2:一個例子
假設以下數據:
..並且偏移量為2
秒,我希望它們按以下方式分組:
第1組:
第2組:
第3組:
您可以創建一個擴展方法來舍入秒數。 我找到了這個例子:讓datetime.now返回到最近的秒
這個問題的答案是:
public static DateTime Trim(this DateTime date, long ticks) {
return new DateTime(date.Ticks - (date.Ticks % ticks), date.Kind);
}
然后,您可以通過以下方式在組中執行以下操作:
var result = dates.GroupBy(x => x.Trim(TimeSpan.FromSeconds(3).Ticks));
所以給出輸入
你會有3組:
A組
B組
C組:
下面的代碼應該可以解決問題。
基本上,它在多個傳遞中進行分組 - 首先在A和B上,然后使用TakeWhile
和Skip
按照您希望對它們進行分組的方式對日期進行分組。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public class SomeItem
{
public int A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
public override string ToString()
{
return $"{A} - {B} - {C}";
}
}
public class Program
{
static void Main(string[] args)
{
// Assuming 2 second margin
var margin = TimeSpan.FromSeconds(2);
var input = new List<SomeItem>
{
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 0)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 2)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 4)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 6)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 9)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 11)},
new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 15)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 2)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 4)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 6)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 9)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 11)},
new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 13)}
};
var firstGrouping = input.GroupBy(x => new { x.A, x.B });
var readyForGrouping = new List<Tuple<SomeItem, int>>();
foreach (var grouping in firstGrouping)
{
var data = grouping.OrderBy(z => z.C).ToList();
var lastDate = default(DateTime?);
var count = 0;
var groupingCount = data.Count();
var groupID = 0;
while (groupingCount > count)
{
groupID++;
readyForGrouping.AddRange(data.Skip(count).TakeWhile(z =>
{
var old = lastDate;
lastDate = z.C;
if (old == null)
{
count++;
return true;
}
if (z.C <= old.Value.Add(margin))
{
count++;
return true;
}
return false;
}).Select(z => new Tuple<SomeItem, int>(z, groupID)).ToList());
}
}
var groupedItems = readyForGrouping.GroupBy(x => new { x.Item1.A, x.Item1.B, x.Item2 },
x => x.Item1);
foreach (var grouping in groupedItems)
{
Console.WriteLine("Start Of Group");
foreach (var entry in grouping)
{
Console.WriteLine(entry);
}
}
Console.ReadLine();
}
}
}
您的示例輸入和輸出有點混亂。
您的前3個項目2,4和6有重疊。 4可以同時屬於2和6.這意味着要決定分組,您需要決定分組將基於哪個項目。 如果你用2開始分組,那么結果就是
似乎你已經應用了你的人類大腦,看看如果起點是4秒,實際上6可以加入第一組。 然后這些團體成為:
您可以創建IEqualityComparer<DateTime>
來執行此操作,但是您的分組依賴於集合的順序:
public class GroupingComparer : IEqualityComparer<DateTime>
{
private readonly int _offset;
public GroupingComparer(int offset)
{
_offset = offset;
}
public bool Equals(DateTime x, DateTime y)
{
if (y.Second >= x.Second - _offset && y.Second <= x.Second + _offset) return true;
return false;
}
public int GetHashCode(DateTime obj)
{
//Should most probably look at a better way to get the hashcode.
return obj.ToShortDateString().GetHashCode();
}
}
像這樣使用它:
GroupingComparer comparer = new GroupingComparer(offset:2);
var result2 = dates.GroupBy(x => x, comparer).ToList();
所以現在一切都取決於你想做什么。 您可以通過更改集合的順序來獲得上述任一輸出。 但是,如果您在應用程序的不同部分使用不同的順序,這可能意味着應用程序中的奇怪行為。 也許一個擴展方法OrderAndGroup
可以解決這個問題。
這是不可能的,因為GroupBy將相同的項目組合在一起。 您可以添加一個“變平”時間的新屬性。
所以說public DateTime FlatDate{get{ return C.ToString("yyyy-MM-dd HH:mm");}}
您可以根據您想要分組的方式更改返回值,因此如果秒<3將其設置為0,則<6將其設置為3等。
不是最優雅,但會做你想要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.