C# 我怎样才能使用;Linq to Objects“;将一组连续的日期放在一个组中?
我有一个棘手的问题要写。我目前正在写一些讨厌的for循环来解决这个问题,但我很想知道Linq是否能帮我解决这个问题 我有:C# 我怎样才能使用;Linq to Objects“;将一组连续的日期放在一个组中?,c#,linq,C#,Linq,我有一个棘手的问题要写。我目前正在写一些讨厌的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<TheS
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();
List orderedList=unorderedList.OrderBy(x=>x.date.ToList();
如果将orderedList结构日期放在一个集合中,它们将始终与日期相邻。。也就是说,如果列表中最晚的日期是2011/01/31,而列表中最早的日期是2011/01/01,那么您会发现该列表将包含31个项目,每个项目对应于1月份的一个日期
好的,我要做的是对列表项进行分组,以便:
谢谢 您可以使用(见下文)按顺序对相邻项目进行分组: 例如:
(1,1)2011-01-01\
(1,1)2011-01-02>第一组
(1,1) 2011-01-03 __/
(2,1) 2011-01-04 \
(2,1)2011-01-05>第2组
(2,1) 2011-01-06 __/
(1,1) 2011-01-07 \
(1,1)2011-01-08>第三组
(1,1) 2011-01-09 __/
(1,1) 2011-02-01 \
(1,1)2011-02-02>第4组
(1,1) 2011-02-03 __/
扩展方法:
静态IEnumerable组(
此IEnumerable源,Func(相邻)
{
var g=新列表();
foreach(源中的变量x)
{
如果(g.Count!=0&&!相邻(g,x))
{
收益率g;
g=新列表();
}
g、 加(x);
}
收益率g;
}
如果我能很好地理解你,一个简单的小组成员就可以做到:
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
希望这有帮助。
干杯这里是“最复杂的方法”的条目:
公共静态类结构管理器
{
公共静态IEnumerable OrganizeWithoutGaps(此IEnumerable someStructs)
{
var somestructsalist=someStructs.ToList();
var lastValuesSeen=新元组(someStructsAsList[0].A,someStructsAsList[0].B);
var currentList=新列表();
返回可枚举
.Range(0,someStructsAsList.Count)
托利斯先生()
.选择(i=>
{
var current=someStructsAsList[i];
if(lastValuesSeen.Equals(新元组(current.A,current.B)))
currentList.Add(当前);
其他的
{
lastValuesSeen=新元组(当前.A,当前.B);
var oldList=currentList;
currentList=新列表(new[]{current});
返回新元组(lastValuesSeen.Item1、lastValuesSeen.Item2、oldList);
}
返回null;
})
。其中(i=>i!=null);
}
//要测试:
公共静态无效测试()
{
var r=新的随机变量();
var sampleData=Enumerable.Range(1,31)。选择(i=>newmystruct{A=r.Next(0,2),B=r.Next(0,2),date=newdatetime(2011,12,i)});
var sortedData=sampleData.OrganizeWithoutGaps();
Console.Out.WriteLine(“示例数据:”);
sampleData.ForEach(s=>Console.Out.WriteLine(“{0}=({1},{2})”,s.date,s.A,s.B));
Console.Out.WriteLine(“输出:”);
sortedData.ToList().ForEach(s=>Console.Out.WriteLine(“({0},{1})={2}”,s.Item1,s.Item2,String.Join(“,”,s.Item3.Select(st=>st.date)));
}
}
假设您有12月1日至12月10日的值(1,1),12月11日至12月20日的值(1,2),以及12月21日至12月31日的值(1,1),您想要两组还是三组输出?如果是两个,您希望(1,1)日期之间有一个间隔,还是缺少日期的默认值a和B?我想要三组。如果存在间隙,则无法形成连续日期集。如果我在间隙中用默认值填充A和B,那么所有组中的项目都会比我最初使用的多,因此您希望GroupBy
A
和B
。然后,在每个组内,是否按订购人日期
?或者你也需要它们是不同的吗?我需要按A和B分组,但困难的是我如何告诉Linq返回的组中的日期必须是连续的?这似乎是我想要的。我不知道的是如何将“相邻”日期分组。我要试试看!这将起作用,但我认为它可能效率很低,因为在相邻的函数中使用了IEnumerable。如果我没有弄错的话,这将导致对那些.Last()调用的重复列表迭代,这将类似于2N!迭代,如果您的所有项都有相同的A和B。也就是说,它比我的解决方案优雅50倍:-)@ChrisShain:如果我没有弄错的话,最后一个()方法非常聪明,可以检查枚举是否是IList。如果是,它将使用source[source.Count-1]
获取最后一项,而不是迭代整个序列。只需在reflector中进行检查-您完全正确。我收回我先前的评论:-)如果1,2发生在第1、2和3天,以及第6、7和9天,这会返回两组1,2吗?这不起作用,它只按数字分组,不管日期是什么。是的,它按数字分组。我不太明白你的要求。你一定是很快就想到了那个@国际广播公司
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
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))));
}
}