Search code examples
c#linq

Group by date range , count and sort within each group LINQ


I have a collection of dates stored in my object. This is sample data. In real time, the dates will come from a service call and I will have no idea what dates and how many will be returned:

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

I now need to group by date range like so:

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

this is my LINQ statement which doesn't achieve what I need but i think i'm in the ballpark (be kind if I'm not):

var defaultGroups = from l in ListHeader
                group l by l.EntryDate into g
                orderby g.Min(x => x.EntryDate)
                select new { GroupBy = g };

This groups by individual dates, so I have 6 groups with 1 date in each. How do I group by date range , count and sort within each group?


Solution

  • Introduce array, which contains ranges you want to group by. Here is two ranges - today (zero days) and 14 days (two weeks):

    var today = DateTime.Today;
    var ranges = new List<int?> { 0, 14 };
    

    Now group your items by range it falls into. If there is no appropriate range (all dates more than two weeks) then default null range value will be used:

    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: Adding custom ranges for grouping:

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