按日期范围分组,在每个 LINQ 组中计数和排序
本文关键字:LINQ 排序 范围 日期 | 更新日期: 2023-09-27 18:31:49
我的对象中存储了一个日期集合。这是示例数据。实时,日期将来自服务呼叫,我不知道将返回哪些日期和数量:
var ListHeader = new List<ListHeaderData>
{
new ListHeaderData
{
EntryDate = new DateTime(2013, 8, 26)
},
new ListHeaderData
{
EntryDate = new DateTime(2013, 9, 11)
},
new ListHeaderData
{
EntryDate = new DateTime(2013, 1, 1)
},
new ListHeaderData
{
EntryDate = new DateTime(2013, 9, 15)
},
new ListHeaderData
{
EntryDate = new DateTime(2013, 9, 17)
},
new ListHeaderData
{
EntryDate = new DateTime(2013, 9, 5)
},
};
我现在需要按日期范围分组,如下所示:
Today (1) <- contains the date 9/17/2013 and count of 1
within 2 weeks (3) <- contains dates 9/15,9/11,9/5 and count of 3
More than 2 weeks (2) <- contains dates 8/26, 1/1 and count of 2
这是我的 LINQ 语句,它没有达到我需要的,但我认为我在球场上(如果我不是,请善待):
var defaultGroups = from l in ListHeader
group l by l.EntryDate into g
orderby g.Min(x => x.EntryDate)
select new { GroupBy = g };
这按单独的日期分组,所以我有 6 组,每组 1 个日期。如何按日期范围分组,在每个组中计数和排序?
引入数组,其中包含要作为分组依据的范围。这是两个范围 - 今天(零天)和 14 天(两周):
var today = DateTime.Today;
var ranges = new List<int?> { 0, 14 };
现在按项目所属的范围对项目进行分组。如果没有合适的范围(所有日期都超过两周),则将使用默认null
范围值:
var defaultGroups =
from h in ListHeader
let daysFromToday = (int)(today - h.EntryDate).TotalDays
group h by ranges.FirstOrDefault(range => daysFromToday <= range) into g
orderby g.Min(x => x.EntryDate)
select g;
更新:添加用于分组的自定义范围:
var ranges = new List<int?>();
ranges.Add(0); // today
ranges.Add(7*2); // two weeks
ranges.Add(DateTime.Today.Day); // within current month
ranges.Add(DateTime.Today.DayOfYear); // within current year
ranges.Sort();
这样做怎么样?
引入一个新属性进行分组并按该属性进行分组。
class ListHeaderData
{
public DateTime EntryDate;
public int DateDifferenceFromToday
{
get
{
TimeSpan difference = DateTime.Today - EntryDate.Date;
if (difference.TotalDays == 0)//today
{
return 1;
}
else if (difference.TotalDays <= 14)//less than 2 weeks
{
return 2;
}
else
{
return 3;//something else
}
}
}
}
编辑:正如@servy评论中指出的那样,其他开发人员可能会混淆使用enum
int
会更具可读性。
所以,你的类的修改版本看起来像这样
class ListHeaderData
{
public DateTime EntryDate;
public DateRange DateDifferenceFromToday
{
get
{
//I think for this version no comments needed names are self explanatory
TimeSpan difference = DateTime.Today - EntryDate.Date;
if (difference.TotalDays == 0)
{
return DateRange.Today;
}
else if (difference.TotalDays <= 14)
{
return DateRange.LessThanTwoWeeks;
}
else
{
return DateRange.MoreThanTwoWeeks;
}
}
}
}
enum DateRange
{
None = 0,
Today = 1,
LessThanTwoWeeks = 2,
MoreThanTwoWeeks = 3
}
并像这样使用它
var defaultGroups = from l in ListHeader
group l by l.DateDifferenceFromToday into g // <--Note group by DateDifferenceFromToday
orderby g.Min(x => x.EntryDate)
select new { GroupBy = g };
您是否特别想以这种方式实现解决方案?另外,您是否真的想在类中引入虚假属性以满足这些要求?
这三行将满足您的要求,并且对于大型集合将更具性能。
var todays = listHeader.Where(item => item.EntryDate == DateTime.Today);
var twoWeeks = listHeader.Where(item => item.EntryDate < DateTime.Today.AddDays(-1)
&& item.EntryDate >= DateTime.Today.AddDays(-14));
var later = listHeader.Where(item => item.EntryDate < DateTime.Today.AddDays(-14));
此外,您还可以获得不同分组的灵活性,而不会影响您的班级。
[编辑:响应排序查询]
利用上面提供的枚举,您可以应用 Union 子句和 OrderBy 子句 Linq 扩展方法,如下所示:
var ord = todays.Select(item => new {Group = DateRange.Today, item.EntryDate})
.Union(
twoWeeks.Select(item => new {Group = DateRange.LessThanTwoWeeks, item.EntryDate}))
.Union(
later.Select(item => new {Group = DateRange.MoreThanTwoWeeks, item.EntryDate}))
.OrderBy(item => item.Group);
请注意,我通过 Linq Select 和匿名类添加分组,以再次动态推送 Group 属性,而不会影响原始类。这将基于原始帖子生成以下输出:
组条目日期今天 17/09/2013 00:00:00少于两周 11/09/2013 00:00:00少于两周 15/09/2013 00:00:00少于两周 05/09/2013 00:00:00超过两周 26/08/2013 00:00:00超过两周 01/01/2013 00:00:00
要获取带有计数的分组日期范围,请执行以下操作:
var ord = todays.Select(item => new {Group = DateRange.Today, Count=todays.Count()})
.Union(
twoWeeks.Select(item => new {Group = DateRange.LessThanTwoWeeks, Count=twoWeeks.Count()}))
.Union(
later.Select(item => new {Group = DateRange.MoreThanTwoWeeks, Count=later.Count()}))
.OrderBy(item => item.Group);
输出为:
组计数今天 1少于两周 3超过两周 2
这取决于你计划使用它的程度。我有很多报告要生成,所以我创建了一个模型IncrementDateRange
,StartTime
、EndTime
和TimeIncrement
作为枚举。
增量处理程序具有许多基于开关的函数,根据小时/天/周/月/季度/年等在开始和结束范围之间吐出时间列表。
然后你得到你的IncrementDateRange
列表,在 linq 中
TotalsList = times.Select(t => new RetailSalesTotalsListItem()
{
IncrementDateRange = t,
Total = storeSales.Where(s => s.DatePlaced >= t.StartTime && s.DatePlaced <= t.EndTime).Sum(s => s.Subtotal),
})
或
TotalsList = storeSales.GroupBy(g => g.IncrementDateRange.StartTime).Select(gg => new RetailSalesTotalsListItem()
{
IncrementDateRange = times.First(t => t.StartTime == gg.Key),
Total = gg.Sum(rs => rs.Subtotal),
}).ToList(),