loop through IEnumerable and group by date and time span differences












1














I have an IEnumerable object that I created from a LINQ query.



The data would look something like this:



Id        EventName        EventDate        EventStart      EventEnd
--------------------------------------------------------------------
1 StoryTime 4/6/2018 8:00 8:45
2 Baking 4/6/2018 8:55 9:30
3 Cooking 4/7/2018 7:45 9:50
4 Comprehension 4/8/2018 9:05 10:10
5 WindDown 4/8/2018 10:25 10:55
6 Naptime 4/8/2018 11:00 11:30
7 Play 4/8/2018 13:50 14:20
8 Smarts 4/8/2018 14:30 16:00
9 StoryTime 4/9/2018 9:30 12:05
10 FunTime 4/10/2018 14:10 16:10


I need to loop through the IEnumerable and examine the data by checking the dates and times. I want to group together events that are on the same day and where the Event's EventStart time is no more than 30 minutes from the previous activities EventEnd time.



The logic is tough. I've been trying different things, but I can't find any way of doing this.



Here is where I have gotten so far:



// loop through IEnumerable object created with linq query
foreach (var e in eventResults)
{
// set current interation current date
DateTime? currentDate = e.EventDate;

// make sure we are only checking time span differences in the same day
while (e.EventDate == currentDate)
{
int currentId = e.Id;
DateTime? currentStartTime = e.EventStart;
DateTime? currentEndTime = e.EventEnd;

// stuck -- not sure where to go with my logic :(
}
}


When it's all done, it would look something like this:




  • On 4/6, StoryTime + Baking: 8:00 - 9:30

  • On 4/7, Cooking: 7:45 - 9:50

  • On 4/8, Comprehension + WindDown + Naptime: 9:05 - 11:30

  • On 4/8, Play + Smarts: 13:50 - 16:00

  • On 4/9, StoryTime: 9:30 - 12:05

  • On 4/10, FunTime: 14:10 - 16:10


I'd greatly appreciate it if someone could provide some help. Thanks!










share|improve this question


















  • 1




    As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
    – grek40
    Nov 12 '18 at 21:54
















1














I have an IEnumerable object that I created from a LINQ query.



The data would look something like this:



Id        EventName        EventDate        EventStart      EventEnd
--------------------------------------------------------------------
1 StoryTime 4/6/2018 8:00 8:45
2 Baking 4/6/2018 8:55 9:30
3 Cooking 4/7/2018 7:45 9:50
4 Comprehension 4/8/2018 9:05 10:10
5 WindDown 4/8/2018 10:25 10:55
6 Naptime 4/8/2018 11:00 11:30
7 Play 4/8/2018 13:50 14:20
8 Smarts 4/8/2018 14:30 16:00
9 StoryTime 4/9/2018 9:30 12:05
10 FunTime 4/10/2018 14:10 16:10


I need to loop through the IEnumerable and examine the data by checking the dates and times. I want to group together events that are on the same day and where the Event's EventStart time is no more than 30 minutes from the previous activities EventEnd time.



The logic is tough. I've been trying different things, but I can't find any way of doing this.



Here is where I have gotten so far:



// loop through IEnumerable object created with linq query
foreach (var e in eventResults)
{
// set current interation current date
DateTime? currentDate = e.EventDate;

// make sure we are only checking time span differences in the same day
while (e.EventDate == currentDate)
{
int currentId = e.Id;
DateTime? currentStartTime = e.EventStart;
DateTime? currentEndTime = e.EventEnd;

// stuck -- not sure where to go with my logic :(
}
}


When it's all done, it would look something like this:




  • On 4/6, StoryTime + Baking: 8:00 - 9:30

  • On 4/7, Cooking: 7:45 - 9:50

  • On 4/8, Comprehension + WindDown + Naptime: 9:05 - 11:30

  • On 4/8, Play + Smarts: 13:50 - 16:00

  • On 4/9, StoryTime: 9:30 - 12:05

  • On 4/10, FunTime: 14:10 - 16:10


I'd greatly appreciate it if someone could provide some help. Thanks!










share|improve this question


















  • 1




    As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
    – grek40
    Nov 12 '18 at 21:54














1












1








1







I have an IEnumerable object that I created from a LINQ query.



The data would look something like this:



Id        EventName        EventDate        EventStart      EventEnd
--------------------------------------------------------------------
1 StoryTime 4/6/2018 8:00 8:45
2 Baking 4/6/2018 8:55 9:30
3 Cooking 4/7/2018 7:45 9:50
4 Comprehension 4/8/2018 9:05 10:10
5 WindDown 4/8/2018 10:25 10:55
6 Naptime 4/8/2018 11:00 11:30
7 Play 4/8/2018 13:50 14:20
8 Smarts 4/8/2018 14:30 16:00
9 StoryTime 4/9/2018 9:30 12:05
10 FunTime 4/10/2018 14:10 16:10


I need to loop through the IEnumerable and examine the data by checking the dates and times. I want to group together events that are on the same day and where the Event's EventStart time is no more than 30 minutes from the previous activities EventEnd time.



The logic is tough. I've been trying different things, but I can't find any way of doing this.



Here is where I have gotten so far:



// loop through IEnumerable object created with linq query
foreach (var e in eventResults)
{
// set current interation current date
DateTime? currentDate = e.EventDate;

// make sure we are only checking time span differences in the same day
while (e.EventDate == currentDate)
{
int currentId = e.Id;
DateTime? currentStartTime = e.EventStart;
DateTime? currentEndTime = e.EventEnd;

// stuck -- not sure where to go with my logic :(
}
}


When it's all done, it would look something like this:




  • On 4/6, StoryTime + Baking: 8:00 - 9:30

  • On 4/7, Cooking: 7:45 - 9:50

  • On 4/8, Comprehension + WindDown + Naptime: 9:05 - 11:30

  • On 4/8, Play + Smarts: 13:50 - 16:00

  • On 4/9, StoryTime: 9:30 - 12:05

  • On 4/10, FunTime: 14:10 - 16:10


I'd greatly appreciate it if someone could provide some help. Thanks!










share|improve this question













I have an IEnumerable object that I created from a LINQ query.



The data would look something like this:



Id        EventName        EventDate        EventStart      EventEnd
--------------------------------------------------------------------
1 StoryTime 4/6/2018 8:00 8:45
2 Baking 4/6/2018 8:55 9:30
3 Cooking 4/7/2018 7:45 9:50
4 Comprehension 4/8/2018 9:05 10:10
5 WindDown 4/8/2018 10:25 10:55
6 Naptime 4/8/2018 11:00 11:30
7 Play 4/8/2018 13:50 14:20
8 Smarts 4/8/2018 14:30 16:00
9 StoryTime 4/9/2018 9:30 12:05
10 FunTime 4/10/2018 14:10 16:10


I need to loop through the IEnumerable and examine the data by checking the dates and times. I want to group together events that are on the same day and where the Event's EventStart time is no more than 30 minutes from the previous activities EventEnd time.



The logic is tough. I've been trying different things, but I can't find any way of doing this.



Here is where I have gotten so far:



// loop through IEnumerable object created with linq query
foreach (var e in eventResults)
{
// set current interation current date
DateTime? currentDate = e.EventDate;

// make sure we are only checking time span differences in the same day
while (e.EventDate == currentDate)
{
int currentId = e.Id;
DateTime? currentStartTime = e.EventStart;
DateTime? currentEndTime = e.EventEnd;

// stuck -- not sure where to go with my logic :(
}
}


When it's all done, it would look something like this:




  • On 4/6, StoryTime + Baking: 8:00 - 9:30

  • On 4/7, Cooking: 7:45 - 9:50

  • On 4/8, Comprehension + WindDown + Naptime: 9:05 - 11:30

  • On 4/8, Play + Smarts: 13:50 - 16:00

  • On 4/9, StoryTime: 9:30 - 12:05

  • On 4/10, FunTime: 14:10 - 16:10


I'd greatly appreciate it if someone could provide some help. Thanks!







c# linq






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 12 '18 at 21:47









SkyeBoniwellSkyeBoniwell

2,13474488




2,13474488








  • 1




    As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
    – grek40
    Nov 12 '18 at 21:54














  • 1




    As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
    – grek40
    Nov 12 '18 at 21:54








1




1




As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
– grek40
Nov 12 '18 at 21:54




As you say "The logic is tough", so as some CS professor said... complex IT problems can be simplified by adding an additional layer of redirection. So here is your extra layer: First group by day without worrying about the times within a day. Then, within each day, create the subgroups with appropriate times. When done, flatten the group hierarchy.
– grek40
Nov 12 '18 at 21:54












3 Answers
3






active

oldest

votes


















3














Here is some linq approach with group by day and Aggregate to create dynamic sub-groups (lists actually) within each day:



var eventResults = new
{
new EventItem(1, "StoryTime", new DateTime(2018, 4, 6), new TimeSpan(8, 0, 0), new TimeSpan(8, 45, 0)),
new EventItem(2, "Baking", new DateTime(2018, 4, 6), new TimeSpan(8,55, 0), new TimeSpan(9, 30, 0)),
new EventItem(3, "Cooking", new DateTime(2018, 4, 7), new TimeSpan(7,45, 0), new TimeSpan(9, 50, 0)),
new EventItem(4, "Comprehension", new DateTime(2018, 4, 8), new TimeSpan(9, 5, 0), new TimeSpan(10,10, 0)),
new EventItem(5, "WindDown", new DateTime(2018, 4, 8), new TimeSpan(10,25, 0), new TimeSpan(10,55, 0)),
new EventItem(6, "Naptime", new DateTime(2018, 4, 8), new TimeSpan(11,0, 0), new TimeSpan(11,30, 0)),
new EventItem(7, "Play", new DateTime(2018, 4, 8), new TimeSpan(13,50,0), new TimeSpan(14,20, 0)),
new EventItem(8, "Smarts", new DateTime(2018, 4, 8), new TimeSpan(14,30,0), new TimeSpan(16, 0, 0)),
new EventItem(9, "StoryTime", new DateTime(2018, 4, 9), new TimeSpan(9,30, 0), new TimeSpan(12, 5, 0)),
new EventItem(10, "FunTime", new DateTime(2018, 4, 10), new TimeSpan(14,10,0), new TimeSpan(16,10, 0)),
};
var groups = eventResults
.GroupBy(x => x.EventDate)
.SelectMany(g => g.OrderBy(x => x.EventStart)
.Aggregate(new List<List<EventItem>> { new List<EventItem>() }, (l, e) =>
{
if ((e.EventStart - l.Last().Select(x => x.EventEnd).DefaultIfEmpty(e.EventStart).Last()).TotalMinutes <= 30)
{
l.Last().Add(e);
}
else
{
l.Add(new List<EventItem> { e });
}
return l;
})
.Select(x =>
new
{
Date = g.Key,
activities = x
}))
.OrderBy(x => x.Date).ThenBy(x => x.activities.First().EventStart);

foreach (var item in groups)
{
var activities = string.Join(" + ", item.activities.Select(x => x.EventName));
Console.WriteLine($"On {item.Date}, {activities}: {item.activities.First().EventStart} - {item.activities.Last().EventEnd}");
}


The key point is, that the requirement of grouping entries when they are less than X apart is not a suitable grouping condition. So instead there needs to be some sort of iteration over the (sorted) entries within each day.



I don't specifically advocate the usage of Aggregate over a traditional loop here. It's just the way I decided to code this thing. Coding the loop would probably be more beginner friendly (easier to read).






share|improve this answer























  • This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
    – SkyeBoniwell
    Nov 13 '18 at 16:25










  • So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
    – SkyeBoniwell
    Nov 13 '18 at 18:43










  • @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
    – grek40
    Nov 13 '18 at 21:22



















1














I'm not sure how you'd do the entire problem in LINQ but, if you found a way, it might be less readable so it's best, IMHO, to use a partially LINQ-based approach:



var flattened = new List<Event>();
for (int i = events.Count - 1; i > 0; i--)
{
events.TimeOffset = events[i].EventStart - events[i - 1].EventEnd;
}

foreach (var grouping in events.GroupBy(g => g.EventName))
{
var compressed = grouping.First();
foreach (var ev in grouping)
{
if (ev.TimeOffset?.Minutes > 30 ?? false)
{
// In this case, we have flattened as far as we can
// Add the event and start over
flattened.Add(compressed);
compressed = new Event
{
Id = ev.Id,
EventName = ev.EventName,
EventDate = ev.EventDate,
EventStart = ev.EventStart,
EventEnd = ev.EventEnd
};
}
else
{
compressed.Name = $"{compressed.Name} + {ev.Name}";
compressed.EventEnd = ev.EventEnd;
}
}
}


Note that in this case I added a field to Event called TimeOffset but you could accomplish the same thing with a dictionary.






share|improve this answer































    1














    Using an extension GroupByWhile that groups according while a boolean is true, which is based on ScanPair, an implementation of APL's scan operator (like aggregate but returns intermediate results) that works a pair at a time:



    public static class IEnumerableExt {
    // TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
    // PrevKeyItem.Key = Previous Key
    // PrevKeyItem.Value = Previous Item
    // curItem = Current Item
    // returns new Key
    public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
    using (var srce = src.GetEnumerator()) {
    if (srce.MoveNext()) {
    var prevkv = (seedKey, srce.Current);

    while (srce.MoveNext()) {
    yield return prevkv;
    prevkv = (combineFn(prevkv, srce.Current), srce.Current);
    }
    yield return prevkv;
    }
    }
    }

    // bool testFn(T prevItem, T curItem)
    // returns groups by sequential matching bool
    public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
    src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
    .GroupBy(kvp => kvp.Key, kvp => kvp.Value);

    }


    You can easily group your data according to your rules:



    var ts = TimeSpan.FromMinutes(30);

    var grouped = data
    .OrderBy(r => r.EventDate)
    .ThenBy(r => r.EventStart)
    .GroupByWhile((p,n) => p.EventDate.Date == n.EventDate.Date && n.EventStart-p.EventEnd < ts);


    If you need an IEnumerable<List<EventResult>> then you can add



    .Select(rg => rg.ToList())


    to the end, assuming your original data is of type IEnumerable<EventResult>.






    share|improve this answer























    • Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
      – SkyeBoniwell
      Nov 13 '18 at 16:16










    • I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
      – SkyeBoniwell
      Nov 13 '18 at 18:13










    • So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
      – SkyeBoniwell
      Nov 13 '18 at 18:44










    • I added code to make the result IEnumerable<List<EventResult>>.
      – NetMage
      Nov 13 '18 at 20:09






    • 1




      Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
      – NetMage
      Nov 15 '18 at 20:41













    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270578%2floop-through-ienumerable-and-group-by-date-and-time-span-differences%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    3














    Here is some linq approach with group by day and Aggregate to create dynamic sub-groups (lists actually) within each day:



    var eventResults = new
    {
    new EventItem(1, "StoryTime", new DateTime(2018, 4, 6), new TimeSpan(8, 0, 0), new TimeSpan(8, 45, 0)),
    new EventItem(2, "Baking", new DateTime(2018, 4, 6), new TimeSpan(8,55, 0), new TimeSpan(9, 30, 0)),
    new EventItem(3, "Cooking", new DateTime(2018, 4, 7), new TimeSpan(7,45, 0), new TimeSpan(9, 50, 0)),
    new EventItem(4, "Comprehension", new DateTime(2018, 4, 8), new TimeSpan(9, 5, 0), new TimeSpan(10,10, 0)),
    new EventItem(5, "WindDown", new DateTime(2018, 4, 8), new TimeSpan(10,25, 0), new TimeSpan(10,55, 0)),
    new EventItem(6, "Naptime", new DateTime(2018, 4, 8), new TimeSpan(11,0, 0), new TimeSpan(11,30, 0)),
    new EventItem(7, "Play", new DateTime(2018, 4, 8), new TimeSpan(13,50,0), new TimeSpan(14,20, 0)),
    new EventItem(8, "Smarts", new DateTime(2018, 4, 8), new TimeSpan(14,30,0), new TimeSpan(16, 0, 0)),
    new EventItem(9, "StoryTime", new DateTime(2018, 4, 9), new TimeSpan(9,30, 0), new TimeSpan(12, 5, 0)),
    new EventItem(10, "FunTime", new DateTime(2018, 4, 10), new TimeSpan(14,10,0), new TimeSpan(16,10, 0)),
    };
    var groups = eventResults
    .GroupBy(x => x.EventDate)
    .SelectMany(g => g.OrderBy(x => x.EventStart)
    .Aggregate(new List<List<EventItem>> { new List<EventItem>() }, (l, e) =>
    {
    if ((e.EventStart - l.Last().Select(x => x.EventEnd).DefaultIfEmpty(e.EventStart).Last()).TotalMinutes <= 30)
    {
    l.Last().Add(e);
    }
    else
    {
    l.Add(new List<EventItem> { e });
    }
    return l;
    })
    .Select(x =>
    new
    {
    Date = g.Key,
    activities = x
    }))
    .OrderBy(x => x.Date).ThenBy(x => x.activities.First().EventStart);

    foreach (var item in groups)
    {
    var activities = string.Join(" + ", item.activities.Select(x => x.EventName));
    Console.WriteLine($"On {item.Date}, {activities}: {item.activities.First().EventStart} - {item.activities.Last().EventEnd}");
    }


    The key point is, that the requirement of grouping entries when they are less than X apart is not a suitable grouping condition. So instead there needs to be some sort of iteration over the (sorted) entries within each day.



    I don't specifically advocate the usage of Aggregate over a traditional loop here. It's just the way I decided to code this thing. Coding the loop would probably be more beginner friendly (easier to read).






    share|improve this answer























    • This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
      – SkyeBoniwell
      Nov 13 '18 at 16:25










    • So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
      – SkyeBoniwell
      Nov 13 '18 at 18:43










    • @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
      – grek40
      Nov 13 '18 at 21:22
















    3














    Here is some linq approach with group by day and Aggregate to create dynamic sub-groups (lists actually) within each day:



    var eventResults = new
    {
    new EventItem(1, "StoryTime", new DateTime(2018, 4, 6), new TimeSpan(8, 0, 0), new TimeSpan(8, 45, 0)),
    new EventItem(2, "Baking", new DateTime(2018, 4, 6), new TimeSpan(8,55, 0), new TimeSpan(9, 30, 0)),
    new EventItem(3, "Cooking", new DateTime(2018, 4, 7), new TimeSpan(7,45, 0), new TimeSpan(9, 50, 0)),
    new EventItem(4, "Comprehension", new DateTime(2018, 4, 8), new TimeSpan(9, 5, 0), new TimeSpan(10,10, 0)),
    new EventItem(5, "WindDown", new DateTime(2018, 4, 8), new TimeSpan(10,25, 0), new TimeSpan(10,55, 0)),
    new EventItem(6, "Naptime", new DateTime(2018, 4, 8), new TimeSpan(11,0, 0), new TimeSpan(11,30, 0)),
    new EventItem(7, "Play", new DateTime(2018, 4, 8), new TimeSpan(13,50,0), new TimeSpan(14,20, 0)),
    new EventItem(8, "Smarts", new DateTime(2018, 4, 8), new TimeSpan(14,30,0), new TimeSpan(16, 0, 0)),
    new EventItem(9, "StoryTime", new DateTime(2018, 4, 9), new TimeSpan(9,30, 0), new TimeSpan(12, 5, 0)),
    new EventItem(10, "FunTime", new DateTime(2018, 4, 10), new TimeSpan(14,10,0), new TimeSpan(16,10, 0)),
    };
    var groups = eventResults
    .GroupBy(x => x.EventDate)
    .SelectMany(g => g.OrderBy(x => x.EventStart)
    .Aggregate(new List<List<EventItem>> { new List<EventItem>() }, (l, e) =>
    {
    if ((e.EventStart - l.Last().Select(x => x.EventEnd).DefaultIfEmpty(e.EventStart).Last()).TotalMinutes <= 30)
    {
    l.Last().Add(e);
    }
    else
    {
    l.Add(new List<EventItem> { e });
    }
    return l;
    })
    .Select(x =>
    new
    {
    Date = g.Key,
    activities = x
    }))
    .OrderBy(x => x.Date).ThenBy(x => x.activities.First().EventStart);

    foreach (var item in groups)
    {
    var activities = string.Join(" + ", item.activities.Select(x => x.EventName));
    Console.WriteLine($"On {item.Date}, {activities}: {item.activities.First().EventStart} - {item.activities.Last().EventEnd}");
    }


    The key point is, that the requirement of grouping entries when they are less than X apart is not a suitable grouping condition. So instead there needs to be some sort of iteration over the (sorted) entries within each day.



    I don't specifically advocate the usage of Aggregate over a traditional loop here. It's just the way I decided to code this thing. Coding the loop would probably be more beginner friendly (easier to read).






    share|improve this answer























    • This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
      – SkyeBoniwell
      Nov 13 '18 at 16:25










    • So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
      – SkyeBoniwell
      Nov 13 '18 at 18:43










    • @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
      – grek40
      Nov 13 '18 at 21:22














    3












    3








    3






    Here is some linq approach with group by day and Aggregate to create dynamic sub-groups (lists actually) within each day:



    var eventResults = new
    {
    new EventItem(1, "StoryTime", new DateTime(2018, 4, 6), new TimeSpan(8, 0, 0), new TimeSpan(8, 45, 0)),
    new EventItem(2, "Baking", new DateTime(2018, 4, 6), new TimeSpan(8,55, 0), new TimeSpan(9, 30, 0)),
    new EventItem(3, "Cooking", new DateTime(2018, 4, 7), new TimeSpan(7,45, 0), new TimeSpan(9, 50, 0)),
    new EventItem(4, "Comprehension", new DateTime(2018, 4, 8), new TimeSpan(9, 5, 0), new TimeSpan(10,10, 0)),
    new EventItem(5, "WindDown", new DateTime(2018, 4, 8), new TimeSpan(10,25, 0), new TimeSpan(10,55, 0)),
    new EventItem(6, "Naptime", new DateTime(2018, 4, 8), new TimeSpan(11,0, 0), new TimeSpan(11,30, 0)),
    new EventItem(7, "Play", new DateTime(2018, 4, 8), new TimeSpan(13,50,0), new TimeSpan(14,20, 0)),
    new EventItem(8, "Smarts", new DateTime(2018, 4, 8), new TimeSpan(14,30,0), new TimeSpan(16, 0, 0)),
    new EventItem(9, "StoryTime", new DateTime(2018, 4, 9), new TimeSpan(9,30, 0), new TimeSpan(12, 5, 0)),
    new EventItem(10, "FunTime", new DateTime(2018, 4, 10), new TimeSpan(14,10,0), new TimeSpan(16,10, 0)),
    };
    var groups = eventResults
    .GroupBy(x => x.EventDate)
    .SelectMany(g => g.OrderBy(x => x.EventStart)
    .Aggregate(new List<List<EventItem>> { new List<EventItem>() }, (l, e) =>
    {
    if ((e.EventStart - l.Last().Select(x => x.EventEnd).DefaultIfEmpty(e.EventStart).Last()).TotalMinutes <= 30)
    {
    l.Last().Add(e);
    }
    else
    {
    l.Add(new List<EventItem> { e });
    }
    return l;
    })
    .Select(x =>
    new
    {
    Date = g.Key,
    activities = x
    }))
    .OrderBy(x => x.Date).ThenBy(x => x.activities.First().EventStart);

    foreach (var item in groups)
    {
    var activities = string.Join(" + ", item.activities.Select(x => x.EventName));
    Console.WriteLine($"On {item.Date}, {activities}: {item.activities.First().EventStart} - {item.activities.Last().EventEnd}");
    }


    The key point is, that the requirement of grouping entries when they are less than X apart is not a suitable grouping condition. So instead there needs to be some sort of iteration over the (sorted) entries within each day.



    I don't specifically advocate the usage of Aggregate over a traditional loop here. It's just the way I decided to code this thing. Coding the loop would probably be more beginner friendly (easier to read).






    share|improve this answer














    Here is some linq approach with group by day and Aggregate to create dynamic sub-groups (lists actually) within each day:



    var eventResults = new
    {
    new EventItem(1, "StoryTime", new DateTime(2018, 4, 6), new TimeSpan(8, 0, 0), new TimeSpan(8, 45, 0)),
    new EventItem(2, "Baking", new DateTime(2018, 4, 6), new TimeSpan(8,55, 0), new TimeSpan(9, 30, 0)),
    new EventItem(3, "Cooking", new DateTime(2018, 4, 7), new TimeSpan(7,45, 0), new TimeSpan(9, 50, 0)),
    new EventItem(4, "Comprehension", new DateTime(2018, 4, 8), new TimeSpan(9, 5, 0), new TimeSpan(10,10, 0)),
    new EventItem(5, "WindDown", new DateTime(2018, 4, 8), new TimeSpan(10,25, 0), new TimeSpan(10,55, 0)),
    new EventItem(6, "Naptime", new DateTime(2018, 4, 8), new TimeSpan(11,0, 0), new TimeSpan(11,30, 0)),
    new EventItem(7, "Play", new DateTime(2018, 4, 8), new TimeSpan(13,50,0), new TimeSpan(14,20, 0)),
    new EventItem(8, "Smarts", new DateTime(2018, 4, 8), new TimeSpan(14,30,0), new TimeSpan(16, 0, 0)),
    new EventItem(9, "StoryTime", new DateTime(2018, 4, 9), new TimeSpan(9,30, 0), new TimeSpan(12, 5, 0)),
    new EventItem(10, "FunTime", new DateTime(2018, 4, 10), new TimeSpan(14,10,0), new TimeSpan(16,10, 0)),
    };
    var groups = eventResults
    .GroupBy(x => x.EventDate)
    .SelectMany(g => g.OrderBy(x => x.EventStart)
    .Aggregate(new List<List<EventItem>> { new List<EventItem>() }, (l, e) =>
    {
    if ((e.EventStart - l.Last().Select(x => x.EventEnd).DefaultIfEmpty(e.EventStart).Last()).TotalMinutes <= 30)
    {
    l.Last().Add(e);
    }
    else
    {
    l.Add(new List<EventItem> { e });
    }
    return l;
    })
    .Select(x =>
    new
    {
    Date = g.Key,
    activities = x
    }))
    .OrderBy(x => x.Date).ThenBy(x => x.activities.First().EventStart);

    foreach (var item in groups)
    {
    var activities = string.Join(" + ", item.activities.Select(x => x.EventName));
    Console.WriteLine($"On {item.Date}, {activities}: {item.activities.First().EventStart} - {item.activities.Last().EventEnd}");
    }


    The key point is, that the requirement of grouping entries when they are less than X apart is not a suitable grouping condition. So instead there needs to be some sort of iteration over the (sorted) entries within each day.



    I don't specifically advocate the usage of Aggregate over a traditional loop here. It's just the way I decided to code this thing. Coding the loop would probably be more beginner friendly (easier to read).







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 12 '18 at 22:27

























    answered Nov 12 '18 at 22:21









    grek40grek40

    10.3k11135




    10.3k11135












    • This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
      – SkyeBoniwell
      Nov 13 '18 at 16:25










    • So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
      – SkyeBoniwell
      Nov 13 '18 at 18:43










    • @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
      – grek40
      Nov 13 '18 at 21:22


















    • This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
      – SkyeBoniwell
      Nov 13 '18 at 16:25










    • So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
      – SkyeBoniwell
      Nov 13 '18 at 18:43










    • @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
      – grek40
      Nov 13 '18 at 21:22
















    This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
    – SkyeBoniwell
    Nov 13 '18 at 16:25




    This looks great, thank you! At the very end, in the foreach (var item in groups), how would I get the results of the looping into an IEnumerable object instead of writing out to console? Thanks again!
    – SkyeBoniwell
    Nov 13 '18 at 16:25












    So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
    – SkyeBoniwell
    Nov 13 '18 at 18:43




    So I got it to run without errors, but it's doing something strange. It's returning a list, but the list is missing the appended events. For example, if I'm expecting, "StoryTime + Baking", I'm only seeing StoryTime and Baking is missing.
    – SkyeBoniwell
    Nov 13 '18 at 18:43












    @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
    – grek40
    Nov 13 '18 at 21:22




    @SkyeBoniwell You can append an Edit section to your question, where you show your solution approach based on my answer. It's probably just a tiny adjustion but it's hard for me to understand your specific problem from this comment alone.
    – grek40
    Nov 13 '18 at 21:22













    1














    I'm not sure how you'd do the entire problem in LINQ but, if you found a way, it might be less readable so it's best, IMHO, to use a partially LINQ-based approach:



    var flattened = new List<Event>();
    for (int i = events.Count - 1; i > 0; i--)
    {
    events.TimeOffset = events[i].EventStart - events[i - 1].EventEnd;
    }

    foreach (var grouping in events.GroupBy(g => g.EventName))
    {
    var compressed = grouping.First();
    foreach (var ev in grouping)
    {
    if (ev.TimeOffset?.Minutes > 30 ?? false)
    {
    // In this case, we have flattened as far as we can
    // Add the event and start over
    flattened.Add(compressed);
    compressed = new Event
    {
    Id = ev.Id,
    EventName = ev.EventName,
    EventDate = ev.EventDate,
    EventStart = ev.EventStart,
    EventEnd = ev.EventEnd
    };
    }
    else
    {
    compressed.Name = $"{compressed.Name} + {ev.Name}";
    compressed.EventEnd = ev.EventEnd;
    }
    }
    }


    Note that in this case I added a field to Event called TimeOffset but you could accomplish the same thing with a dictionary.






    share|improve this answer




























      1














      I'm not sure how you'd do the entire problem in LINQ but, if you found a way, it might be less readable so it's best, IMHO, to use a partially LINQ-based approach:



      var flattened = new List<Event>();
      for (int i = events.Count - 1; i > 0; i--)
      {
      events.TimeOffset = events[i].EventStart - events[i - 1].EventEnd;
      }

      foreach (var grouping in events.GroupBy(g => g.EventName))
      {
      var compressed = grouping.First();
      foreach (var ev in grouping)
      {
      if (ev.TimeOffset?.Minutes > 30 ?? false)
      {
      // In this case, we have flattened as far as we can
      // Add the event and start over
      flattened.Add(compressed);
      compressed = new Event
      {
      Id = ev.Id,
      EventName = ev.EventName,
      EventDate = ev.EventDate,
      EventStart = ev.EventStart,
      EventEnd = ev.EventEnd
      };
      }
      else
      {
      compressed.Name = $"{compressed.Name} + {ev.Name}";
      compressed.EventEnd = ev.EventEnd;
      }
      }
      }


      Note that in this case I added a field to Event called TimeOffset but you could accomplish the same thing with a dictionary.






      share|improve this answer


























        1












        1








        1






        I'm not sure how you'd do the entire problem in LINQ but, if you found a way, it might be less readable so it's best, IMHO, to use a partially LINQ-based approach:



        var flattened = new List<Event>();
        for (int i = events.Count - 1; i > 0; i--)
        {
        events.TimeOffset = events[i].EventStart - events[i - 1].EventEnd;
        }

        foreach (var grouping in events.GroupBy(g => g.EventName))
        {
        var compressed = grouping.First();
        foreach (var ev in grouping)
        {
        if (ev.TimeOffset?.Minutes > 30 ?? false)
        {
        // In this case, we have flattened as far as we can
        // Add the event and start over
        flattened.Add(compressed);
        compressed = new Event
        {
        Id = ev.Id,
        EventName = ev.EventName,
        EventDate = ev.EventDate,
        EventStart = ev.EventStart,
        EventEnd = ev.EventEnd
        };
        }
        else
        {
        compressed.Name = $"{compressed.Name} + {ev.Name}";
        compressed.EventEnd = ev.EventEnd;
        }
        }
        }


        Note that in this case I added a field to Event called TimeOffset but you could accomplish the same thing with a dictionary.






        share|improve this answer














        I'm not sure how you'd do the entire problem in LINQ but, if you found a way, it might be less readable so it's best, IMHO, to use a partially LINQ-based approach:



        var flattened = new List<Event>();
        for (int i = events.Count - 1; i > 0; i--)
        {
        events.TimeOffset = events[i].EventStart - events[i - 1].EventEnd;
        }

        foreach (var grouping in events.GroupBy(g => g.EventName))
        {
        var compressed = grouping.First();
        foreach (var ev in grouping)
        {
        if (ev.TimeOffset?.Minutes > 30 ?? false)
        {
        // In this case, we have flattened as far as we can
        // Add the event and start over
        flattened.Add(compressed);
        compressed = new Event
        {
        Id = ev.Id,
        EventName = ev.EventName,
        EventDate = ev.EventDate,
        EventStart = ev.EventStart,
        EventEnd = ev.EventEnd
        };
        }
        else
        {
        compressed.Name = $"{compressed.Name} + {ev.Name}";
        compressed.EventEnd = ev.EventEnd;
        }
        }
        }


        Note that in this case I added a field to Event called TimeOffset but you could accomplish the same thing with a dictionary.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 13 '18 at 15:52

























        answered Nov 12 '18 at 22:15









        Woody1193Woody1193

        2,246930




        2,246930























            1














            Using an extension GroupByWhile that groups according while a boolean is true, which is based on ScanPair, an implementation of APL's scan operator (like aggregate but returns intermediate results) that works a pair at a time:



            public static class IEnumerableExt {
            // TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
            // PrevKeyItem.Key = Previous Key
            // PrevKeyItem.Value = Previous Item
            // curItem = Current Item
            // returns new Key
            public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
            using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
            var prevkv = (seedKey, srce.Current);

            while (srce.MoveNext()) {
            yield return prevkv;
            prevkv = (combineFn(prevkv, srce.Current), srce.Current);
            }
            yield return prevkv;
            }
            }
            }

            // bool testFn(T prevItem, T curItem)
            // returns groups by sequential matching bool
            public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
            src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
            .GroupBy(kvp => kvp.Key, kvp => kvp.Value);

            }


            You can easily group your data according to your rules:



            var ts = TimeSpan.FromMinutes(30);

            var grouped = data
            .OrderBy(r => r.EventDate)
            .ThenBy(r => r.EventStart)
            .GroupByWhile((p,n) => p.EventDate.Date == n.EventDate.Date && n.EventStart-p.EventEnd < ts);


            If you need an IEnumerable<List<EventResult>> then you can add



            .Select(rg => rg.ToList())


            to the end, assuming your original data is of type IEnumerable<EventResult>.






            share|improve this answer























            • Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
              – SkyeBoniwell
              Nov 13 '18 at 16:16










            • I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
              – SkyeBoniwell
              Nov 13 '18 at 18:13










            • So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
              – SkyeBoniwell
              Nov 13 '18 at 18:44










            • I added code to make the result IEnumerable<List<EventResult>>.
              – NetMage
              Nov 13 '18 at 20:09






            • 1




              Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
              – NetMage
              Nov 15 '18 at 20:41


















            1














            Using an extension GroupByWhile that groups according while a boolean is true, which is based on ScanPair, an implementation of APL's scan operator (like aggregate but returns intermediate results) that works a pair at a time:



            public static class IEnumerableExt {
            // TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
            // PrevKeyItem.Key = Previous Key
            // PrevKeyItem.Value = Previous Item
            // curItem = Current Item
            // returns new Key
            public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
            using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
            var prevkv = (seedKey, srce.Current);

            while (srce.MoveNext()) {
            yield return prevkv;
            prevkv = (combineFn(prevkv, srce.Current), srce.Current);
            }
            yield return prevkv;
            }
            }
            }

            // bool testFn(T prevItem, T curItem)
            // returns groups by sequential matching bool
            public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
            src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
            .GroupBy(kvp => kvp.Key, kvp => kvp.Value);

            }


            You can easily group your data according to your rules:



            var ts = TimeSpan.FromMinutes(30);

            var grouped = data
            .OrderBy(r => r.EventDate)
            .ThenBy(r => r.EventStart)
            .GroupByWhile((p,n) => p.EventDate.Date == n.EventDate.Date && n.EventStart-p.EventEnd < ts);


            If you need an IEnumerable<List<EventResult>> then you can add



            .Select(rg => rg.ToList())


            to the end, assuming your original data is of type IEnumerable<EventResult>.






            share|improve this answer























            • Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
              – SkyeBoniwell
              Nov 13 '18 at 16:16










            • I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
              – SkyeBoniwell
              Nov 13 '18 at 18:13










            • So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
              – SkyeBoniwell
              Nov 13 '18 at 18:44










            • I added code to make the result IEnumerable<List<EventResult>>.
              – NetMage
              Nov 13 '18 at 20:09






            • 1




              Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
              – NetMage
              Nov 15 '18 at 20:41
















            1












            1








            1






            Using an extension GroupByWhile that groups according while a boolean is true, which is based on ScanPair, an implementation of APL's scan operator (like aggregate but returns intermediate results) that works a pair at a time:



            public static class IEnumerableExt {
            // TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
            // PrevKeyItem.Key = Previous Key
            // PrevKeyItem.Value = Previous Item
            // curItem = Current Item
            // returns new Key
            public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
            using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
            var prevkv = (seedKey, srce.Current);

            while (srce.MoveNext()) {
            yield return prevkv;
            prevkv = (combineFn(prevkv, srce.Current), srce.Current);
            }
            yield return prevkv;
            }
            }
            }

            // bool testFn(T prevItem, T curItem)
            // returns groups by sequential matching bool
            public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
            src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
            .GroupBy(kvp => kvp.Key, kvp => kvp.Value);

            }


            You can easily group your data according to your rules:



            var ts = TimeSpan.FromMinutes(30);

            var grouped = data
            .OrderBy(r => r.EventDate)
            .ThenBy(r => r.EventStart)
            .GroupByWhile((p,n) => p.EventDate.Date == n.EventDate.Date && n.EventStart-p.EventEnd < ts);


            If you need an IEnumerable<List<EventResult>> then you can add



            .Select(rg => rg.ToList())


            to the end, assuming your original data is of type IEnumerable<EventResult>.






            share|improve this answer














            Using an extension GroupByWhile that groups according while a boolean is true, which is based on ScanPair, an implementation of APL's scan operator (like aggregate but returns intermediate results) that works a pair at a time:



            public static class IEnumerableExt {
            // TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
            // PrevKeyItem.Key = Previous Key
            // PrevKeyItem.Value = Previous Item
            // curItem = Current Item
            // returns new Key
            public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
            using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
            var prevkv = (seedKey, srce.Current);

            while (srce.MoveNext()) {
            yield return prevkv;
            prevkv = (combineFn(prevkv, srce.Current), srce.Current);
            }
            yield return prevkv;
            }
            }
            }

            // bool testFn(T prevItem, T curItem)
            // returns groups by sequential matching bool
            public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
            src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
            .GroupBy(kvp => kvp.Key, kvp => kvp.Value);

            }


            You can easily group your data according to your rules:



            var ts = TimeSpan.FromMinutes(30);

            var grouped = data
            .OrderBy(r => r.EventDate)
            .ThenBy(r => r.EventStart)
            .GroupByWhile((p,n) => p.EventDate.Date == n.EventDate.Date && n.EventStart-p.EventEnd < ts);


            If you need an IEnumerable<List<EventResult>> then you can add



            .Select(rg => rg.ToList())


            to the end, assuming your original data is of type IEnumerable<EventResult>.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 15 '18 at 20:41

























            answered Nov 13 '18 at 1:06









            NetMageNetMage

            13.2k11834




            13.2k11834












            • Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
              – SkyeBoniwell
              Nov 13 '18 at 16:16










            • I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
              – SkyeBoniwell
              Nov 13 '18 at 18:13










            • So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
              – SkyeBoniwell
              Nov 13 '18 at 18:44










            • I added code to make the result IEnumerable<List<EventResult>>.
              – NetMage
              Nov 13 '18 at 20:09






            • 1




              Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
              – NetMage
              Nov 15 '18 at 20:41




















            • Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
              – SkyeBoniwell
              Nov 13 '18 at 16:16










            • I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
              – SkyeBoniwell
              Nov 13 '18 at 18:13










            • So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
              – SkyeBoniwell
              Nov 13 '18 at 18:44










            • I added code to make the result IEnumerable<List<EventResult>>.
              – NetMage
              Nov 13 '18 at 20:09






            • 1




              Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
              – NetMage
              Nov 15 '18 at 20:41


















            Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
            – SkyeBoniwell
            Nov 13 '18 at 16:16




            Thanks! Do I need to import any external libraries to use this extension? I can't seem to find it.
            – SkyeBoniwell
            Nov 13 '18 at 16:16












            I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
            – SkyeBoniwell
            Nov 13 '18 at 18:13




            I actually just figured out that the class should outside the main class. So I have implemented the extension method, but "grouped" is of type IEnumerable<IGrouping<int, EventResult>>. How would I convert that to a List of <EventResult> ? Thanks!
            – SkyeBoniwell
            Nov 13 '18 at 18:13












            So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
            – SkyeBoniwell
            Nov 13 '18 at 18:44




            So I figured out how to convert it back to a List, but it's missing the grouping (duh on my part) so I'm back to where I started...
            – SkyeBoniwell
            Nov 13 '18 at 18:44












            I added code to make the result IEnumerable<List<EventResult>>.
            – NetMage
            Nov 13 '18 at 20:09




            I added code to make the result IEnumerable<List<EventResult>>.
            – NetMage
            Nov 13 '18 at 20:09




            1




            1




            Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
            – NetMage
            Nov 15 '18 at 20:41






            Are you using eventResults in place of data? What is the type of eventResults? Also, IEnumerable<EventResult> implies no grouping - so what is the point of the grouping in that case? Ordering? Then you need to use SelectMany instead of Select... but I'm pretty sure you just end of with the original data sorted and that's a much easier thing to do.
            – NetMage
            Nov 15 '18 at 20:41




















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270578%2floop-through-ienumerable-and-group-by-date-and-time-span-differences%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Xamarin.form Move up view when keyboard appear

            Post-Redirect-Get with Spring WebFlux and Thymeleaf

            Anylogic : not able to use stopDelay()