如何使用“对象关联”?把一组连续的日期放在一组中
本文关键字:一组 连续 日期 何使用 对象 关联 对象关联 | 更新日期: 2023-09-27 18:16:45
我有一个麻烦的查询要写。我现在正在写一些讨厌的for循环来解决这个问题,但是我很想知道Linq是否可以为我做这件事。
我:
struct TheStruct
{
public DateTime date {get; set;} //(time portion will always be 12 am)
public decimal A {get; set;}
public decimal B {get; set;}
}
和包含这些结构体的列表。假设它是这样排列的:
List<TheStruct> orderedList = unorderedList.OrderBy(x => x.date).ToList();
如果你把orderedList结构体日期放在一个集合中,它们将总是在日期方面连续。也就是说,如果列表中最晚的日期是2011年1月31日,而列表中最早的日期是2011年1月1日,那么您会发现列表将包含31个条目,每个条目对应1月的日期。
好的,我要做的是将列表项分组如下:
- 组内各条目必须包含相同的十进制a值和十进制B值
- 如果日期值顺序 ,则组中的日期值必须形成一组连续的日期。
- 如果你把每组中的项目加起来,总数等于原始列表中的项目数量(或者你可以说具有特定日期的结构不能属于多个组)
有Linq高手知道怎么做这个吗?
谢谢!
您可以使用groupadjacned扩展方法(见下文)将相邻的项按顺序分组:
var result = unorderedList
.OrderBy(x => x.date)
.GroupAdjacent((g, x) => x.A == g.Last().A &&
x.B == g.Last().B &&
x.date == g.Last().date.AddDays(1))
.ToList();
例子:
(1,1) 2011-01-01 '
(1,1) 2011-01-02 > Group 1
(1,1) 2011-01-03 __/
(2,1) 2011-01-04 '
(2,1) 2011-01-05 > Group 2
(2,1) 2011-01-06 __/
(1,1) 2011-01-07 '
(1,1) 2011-01-08 > Group 3
(1,1) 2011-01-09 __/
(1,1) 2011-02-01 '
(1,1) 2011-02-02 > Group 4
(1,1) 2011-02-03 __/
扩展方法:
static IEnumerable<IEnumerable<T>> GroupAdjacent<T>(
this IEnumerable<T> source, Func<IEnumerable<T>, T, bool> adjacent)
{
var g = new List<T>();
foreach (var x in source)
{
if (g.Count != 0 && !adjacent(g, x))
{
yield return g;
g = new List<T>();
}
g.Add(x);
}
yield return g;
}
这是"Most Convoluted way to do this"的条目:
public static class StructOrganizer
{
public static IEnumerable<Tuple<Decimal, Decimal, IEnumerable<MyStruct>>> OrganizeWithoutGaps(this IEnumerable<MyStruct> someStructs)
{
var someStructsAsList = someStructs.ToList();
var lastValuesSeen = new Tuple<Decimal, Decimal>(someStructsAsList[0].A, someStructsAsList[0].B);
var currentList = new List<MyStruct>();
return Enumerable
.Range(0, someStructsAsList.Count)
.ToList()
.Select(i =>
{
var current = someStructsAsList[i];
if (lastValuesSeen.Equals(new Tuple<Decimal, Decimal>(current.A, current.B)))
currentList.Add(current);
else
{
lastValuesSeen = new Tuple<decimal, decimal>(current.A, current.B);
var oldList = currentList;
currentList = new List<MyStruct>(new [] { current });
return new Tuple<decimal, decimal, IEnumerable<MyStruct>>(lastValuesSeen.Item1, lastValuesSeen.Item2, oldList);
}
return null;
})
.Where(i => i != null);
}
// To Test:
public static void Test()
{
var r = new Random();
var sampleData = Enumerable.Range(1, 31).Select(i => new MyStruct {A = r.Next(0, 2), B = r.Next(0, 2), date = new DateTime(2011, 12, i)}).OrderBy(s => s.date).ToList();
var sortedData = sampleData.OrganizeWithoutGaps();
Console.Out.WriteLine("Sample Data:");
sampleData.ForEach(s => Console.Out.WriteLine("{0} = ({1}, {2})", s.date, s.A, s.B));
Console.Out.WriteLine("Output:");
sortedData.ToList().ForEach(s => Console.Out.WriteLine("({0}, {1}) = {2}", s.Item1, s.Item2, String.Join(", ", s.Item3.Select(st => st.date))));
}
}
如果我理解你的话,一个简单的Group By就可以了:
var orderedList = unorderedList.OrderBy(o => o.date).GroupBy(s => new {s.A, s.B});
。打印结果:
foreach (var o in orderedList) {
Console.WriteLine("Dates of group {0},{1}:", o.Key.A, o.Key.B);
foreach(var s in o){
Console.WriteLine("'t{0}", s.date);
}
}
输出如下:
Dates of group 2,3:
02/12/2011
03/12/2011
Dates of group 4,3:
03/12/2011
Dates of group 1,2:
04/12/2011
05/12/2011
06/12/2011
希望这对你有帮助。欢呼声