我之前asked this question收集日期时间对象,并按天和时间分组
所以回顾一下:我收集了DateTime
的集合List<DateTime> collectionOfDateTime = GetDateColletion();
然后通过dayofWeek和时间进行分组
var byDayOfWeek = collectionOfDateTime.GroupBy(dt => dt.DayOfWeek + "-" + dt.Hour + "-" + dt.Minute);
所以在这一点上,我按星期(一致时间)分组这些工作完美。
我现在有了按月而不是按周分组的新要求。当我说“月”时,它不是月份的同一天,而是“每个月的第一个星期二”之类的东西
我试图弄清楚在组中使用什么“关键”来对所有符合月度逻辑的项目进行分组(本月的第一个星期二,每个月的第二个星期五等)
举个例子,我可以说这些日期开始了;
var date1 = new DateTime(2013, 1, 4).AddHours(8); // This is the first Friday in Jan
var date2 = new DateTime(2013, 2, 1).AddHours(8); // This is the first Friday in Feb
var date3 = new DateTime(2013, 1, 5).AddHours(3); // This is the first Sat in Jan
var date4 = new DateTime(2013, 2, 2).AddHours(3); // This is the first Sat in Feb
var date5 = new DateTime(2013, 2, 2).AddHours(6); // This is the first Sat in Feb - different time
如果这些是进入原始数组的日期,我需要一个groupby最终得到3个组。
有人可以建议按照这个标准分组吗?
答案 0 :(得分:2)
我认为它比它看起来容易:
var byDayOfMonth = from d in dates
let h = (d.Day / 7) + 1
group d by new { d.DayOfWeek, h } into g
select g;
本地变量h = (d.Day / 7) + 1
设置实际上该月内的DayOfWeek
。
我运行它进行测试并收到2组,与你的例子完全相同。这些组的关键是:
{ DayOfWeek = Friday, h = 1 }
{ DayOfWeek = Saturday, h = 1 }
什么意思,有'月的第一个星期五'和'月的第一个星期六的群组。
如果您愿意,可以轻松地按d.Hour
和/或d.Minute
扩展分组键:
var byDayOfMonth = from d in dates
let h = (d.Day / 7) + 1
group d by new { d.DayOfWeek, h, d.Hour, d.Minute } into g
select g;
结果(仅限密钥):
{ DayOfWeek = Friday, h = 1, Hour = 8, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 3, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 6, Minute = 0 }
答案 1 :(得分:0)
这可能是一种更简单的方法,但这就是我的想法:
我从你的问题中得知你需要将所有内容从“2月的第一个星期二”到“3月的第一个星期一”等组合在一起,这样你就可以得到这些“月”跨度,这是一个可变的天数 - 取决于他们开始的那个月。如果是这样,那么你真的需要使用一年中的日期将其分解为范围,所以:
Group by the First Wednesday of the Month 2013
Group 0 (0-1)
All DayOfYear between 0 and 1 2013
Group 1 (2-36)
The first Wednesday of the month: January is DayOfYear 2.
The first Wednesday of the month: February is DayOfYear 37.
etc.
所以第一个范围是函数 f ,因此f(32)= 1(DayOfYear是32)因为它落在2到37的范围内。这个 f 是一个索引的范围集合,查找给定DayOfYear所属的集合中的项目,并将该项目的索引作为组编号返回。
您可以通过从GetDateCollection
获取最短和最长日期来动态构建此表,以确定整体范围。因为围绕日期的逻辑本身就是一个相当复杂的主题,我会回到像NodaTime这样的库(特别是arithmetic文档),从最小日期开始,一天一天地推进,直到我找到了第一个符合条件的日期(即“当月的第一个星期一”)并创建了一个范围0到那天 - 1作为组0并将其推送到索引集合(可能ArrayList
)。然后使用LocalDate.PlusWeeks(1)
从该日期开始循环,直到月份发生变化,构建新范围并将该范围推送到相同的索引集合。
每次你进入新的一年,你需要在DayOfYear每年重置之后,在构建你的索引集合时向你的DayOfYear添加365(或者如果前一年是闰年则为366)。
现在你有了一系列作为表的范围,根据DayOfYear将天数分组到所需的单位。
编写一个遍历表格的函数,将给定日期的DayOfYear(+ [365|366] * x
,其中x是您比较的日期的年数)与集合中的项目进行比较,直到找到当天所在的范围,并将该项目的索引作为组号返回。 (或者,如果提供的Func<DateTime,bool>
落在该范围内,则每个范围可以是DateTime
,返回true。)
范围集合的替代数据结构将是一个ushort数组,其长度等于日期范围内从最小日期到最大日期的所有天数,以及每天它们分配的组编号的值(使用范围计算,如上)。这将对分组执行得更快,但如果您使用较小的数据集(只有几百个日期),性能可能不会明显。
答案 2 :(得分:-2)
要使用Linq分组,也许这段代码可以帮助您:
List<DateTime> collectionOfDateTime = GetDateColletion();
collectionOfDateTime.GroupBy(s => Convert.ToInt16(s.DayOfWeek) & s.Hour & s.Minute);