按日期范围分组,在每个组内计数和排序LINQ

tag5nh1u  于 2022-12-06  发布在  其他
关注(0)|答案(4)|浏览(134)

我的对象中存储了一个日期集合。这是示例数据。在真实的情况下,这些日期将来自服务调用,我不知道返回的是什么日期和多少日期:

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个日期。我如何按日期范围分组,在每个组中计数和排序?

sxpgvts3

sxpgvts31#

引入数组,其中包含要作为分组依据的范围。下面是两个范围-今天(零天)和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;

UPDATE:新增群组的自订范围:

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();
vs3odd8k

vs3odd8k2#

这样如何?
为分组引入一个新属性,并按该属性分组。

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在评论中指出的那样,其他开发人员可能会混淆int,使用enum会更具可读性。
因此,修改后的类将如下所示

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 };
blmhpbnm

blmhpbnm3#

您是否特别希望以这种方式实现解决方案?您是否真的希望在类中引入伪属性来满足这些要求?
这三条线可以满足您的要求,对于大型集合,性能会更好。

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));

你还可以灵活地进行不同的分组而不影响你的类。

[编辑:响应订购查询]

利用上面提供的Enum,您可以应用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和匿名类添加了Grouping,以再次动态推送Group属性,而不影响原始类。

**Group            EntryDate**
Today            17/09/2013 00:00:00
LessThanTwoWeeks 11/09/2013 00:00:00
LessThanTwoWeeks 15/09/2013 00:00:00 
LessThanTwoWeeks 05/09/2013 00:00:00 
MoreThanTwoWeeks 26/08/2013 00:00:00 
MoreThanTwoWeeks 01/01/2013 00:00:00

并使用count获取分组的日期范围:

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);

输出为:

**Group            Count**
Today            1 
LessThanTwoWeeks 3 
MoreThanTwoWeeks 2
93ze6v8z

93ze6v8z4#

我想这取决于你计划使用它的程度。我有/有很多报告要生成,所以我用StartTimeEndTimeTimeIncrement作为枚举创建了一个模型IncrementDateRange
时间增量处理程序有很多基于开关的函数,基于小时/天/周/月/季度/年等,在开始和结束范围之间吐出一个时间列表。
然后你得到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(),

相关问题