Primico
Primico

Reputation: 2465

Use LINQ to include items within a range

I have a calendar of events. I use the following LINQ statement to load the events to display in a calendar:

var events = CalendarItems?.GroupBy(e => e.StartDate);

It results in a GroupedEnumerable with key/values like this:

Key = 6/26/2019, Value = CalendarItem
Key = 7/10/2019, Value = CalendarItem
Key = 7/18/2019, Value = CalendarItem

Some CalendarItems have "EndDate" and if it does, I would like each date in the range of StartDate to EndDate to also be included in the result set. So if the 7/18/2019 CalendarItem had an EndDate of 7/20/2019, then my result set would also include these 2 items:

Key = 7/19/2019, Value = CalendarItem
Key = 7/20/2019, Value = CalendarItem

I have no idea how I would modify my LINQ statement. Any recommendations?

Thanks!

So, if I had these 2 CalendarItems:

StartDate = 7/8/2019, EndDate = null
StartDate = 7/18/2019, EndDate = 7/20/2019

Then my result should be:

Key = 7/8/2019, Value = CalendarItem
Key = 7/18/2019, Value = CalendarItem
Key = 7/19/2019, Value = CalendarItem
Key = 7/20/2019, Value = CalendarItem

Upvotes: 0

Views: 74

Answers (2)

JohnyL
JohnyL

Reputation: 7162

class Item
{
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

var items = new List<Item>
{
    new Item {StartDate = new DateTime(2019, 2, 22)},
    new Item {StartDate = new DateTime(2019, 3, 12 )},
    new Item {StartDate = new DateTime(2019, 4, 15), EndDate = new DateTime(2019, 4, 25) },
    new Item {StartDate = new DateTime(2019, 2, 15), EndDate = new DateTime(2019, 2, 17) },
    new Item {StartDate = new DateTime(2019, 2, 23)},
    new Item {StartDate = new DateTime(2019, 2, 7)},
};

var x = items.Select(i => (i.StartDate, diff: Enumerable.Range(0, ((i.EndDate == null ? i.StartDate : i.EndDate).Value - i.StartDate).Days + 1)))
             .Select(x => x.diff.Select(z => x.StartDate.AddDays(z)));

var days = new List<DateTime>();
x.ToList().ForEach(dates => days.AddRange(dates));
// Output:
days.OrderBy(i => i).ToList().ForEach(d => Console.WriteLine(d.ToShortDateString()));


/*
    Output:
    07.02.2019
    15.02.2019
    16.02.2019
    17.02.2019
    22.02.2019
    23.02.2019
    12.03.2019
    15.04.2019
    16.04.2019
    17.04.2019
    18.04.2019
    19.04.2019
    20.04.2019
    21.04.2019
    22.04.2019
    23.04.2019
    24.04.2019
    25.04.2019
*/

Upvotes: 0

Dortimer
Dortimer

Reputation: 617

Try this:

var events = CalendarItems?.Where(x => x.EndDate.HasValue && x.StartDate < x.EndDate).GroupBy(e => e.StartDate);

(For the first check you could also do x.EndDate != null)

The biggest thing to note is just the .Where() clause. You could perform this when looping through the groups as well. So if you need to do the checks after the grouping, you could use this alternatively:

var events = CalendarItems?.GroupBy(e => e.StartDate);

foreach(var event in events)
{
    var validEvent = event.Where(x => x.EndDate.HasValue && x.StartDate < x.EndDate);
}

Functionally they're the same, but if for whatever reason you need to process the other dates, that foreach() will let you pick through what you need on a group by group basis.

Upvotes: 1

Related Questions