I have a scenario where I have a list of objects with a datetime field in the object. I'm trying to find out if there is a way to use LINQ to group the list by sequential datetimes and return a subset of the list with the sequential datetimes as a range.
public virtual IList<LineItem> LineItems { get; set; }
...
public class LineItem
{
    public virtual string Status { get; set; }
    public virtual DateTime TransDate { get; set; }
    ...
}
So If I had 6 LineItems with Status = P for all and 
TransDate = { 8/1/2011 , 8/2/2011 , 8/3/2011 , 8/5/2011 , 8/6/2011 , 8/9/2011 }
respectively, I'd like to return the following list:
{ (P, 8/1/2011-8/3/2011) , (P,8/5/2011-8/6/2011) , (P,8/9/2011) }
Any thoughts? I can do this with iterating through the list manually and checking the TransDate to see if it's sequential, I am just looking for a more elegant (preferably LINQ) way of doing it. Thanks!
I would use a helper method like this:
private static IEnumerable<ICollection<T>> PartitionByPredicate<T>(
    this IEnumerable<T> seq, Func<T, T, bool> split)
{
    var buffer = new List<T>();
    foreach (var x in seq)
    {
        if (buffer.Any() && split(buffer.Last(), x))
        {
            yield return buffer;
            buffer = new List<T>();
        }
        buffer.Add(x);
    }
    if (buffer.Any())
        yield return buffer;
}
And then:
var sorted = LineItems.OrderBy(i => i.TransDate);
var split = sorted.PartitionByPredicate(
    (x, y) => (y.TransDate.Date - x.TransDate.Date).TotalDays > 1)
(edit: cleaned it up slightly, my first version was silly.)
I suggest you go with an iterator block implementation, as suggested by @mquander.
But here's a fun, pure LINQ solution that will work (albeit inefficiently), assuming the dates are distinct and chronological:
var groups = from item in LineItems
             let startDate = item.TransDate
             group item by LineItems.Select(lineItem => lineItem.TransDate)
                                    .SkipWhile(endDate => endDate < startDate)                                        
                                    .TakeWhile((endDate, index) => 
                                                startDate.AddDays(index) == endDate)
                                    .Last();
//If required:
 var groupsAsLists = groups.Select(g => g.ToList()).ToList();
This works by choosing the last sequential date in any date-sequence as the key for that sequence.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With