在我的.NET 4.5.2 C#控制台应用中,我有List<SomeItem>
名为someItems
且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
,但有一个问题:我想将SomeItems
属性在+ - 内的C
组合在一起 - (加或减)彼此相差2秒(基本上是2秒的偏移)。因此,例如,两个SomeItem
,其中一个C
设置为
2016-03-28 17:58:01.000
和2016-03-28 17:58:03.000
的另一个将组合在一起。这可能吗?
编辑:出于所有意图和目的,我们假设列表中不存在可能导致重叠的项目。小组(就像@J.Steen在评论中提出的问题)
编辑2:示例
假设以下数据:
..和2
秒的偏移量,我希望它们按以下方式分组:
第1组:
第2组:
第3组:
答案 0 :(得分:1)
您可以创建一个扩展方法来舍入秒数。我找到了这个例子: Have datetime.now return to the nearest second
该问题的答案是:
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组:
答案 1 :(得分:1)
下面的代码可以解决问题。
基本上,它会在多个传递中进行分组 - 首先在A和B上进行分组,然后使用TakeWhile
和Skip
按照您希望对其进行分组的方式对日期进行分组。< / p>
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();
}
}
}
答案 2 :(得分:1)
您的示例输入和输出有点混乱。
您的前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
可以解决此问题。
答案 3 :(得分:0)
这是不可能的,因为GroupBy将相同的项目组合在一起。你可以添加一个新的属性&#34; flattens&#34;次。
所以说出类似的话
public DateTime FlatDate{get{ return C.ToString("yyyy-MM-dd HH:mm");}}
你可以根据你想要分组的方式来改变回报,所以如果第二个是&lt; 3将其设为0,&lt; 6将其设置为3等。
不是最优雅,但会做你想做的事。