首页 > 解决方案 > 遍历 IEnumerable 并按日期和时间跨度差异分组

问题描述

我有一个从 LINQ 查询创建的 IEnumerable 对象。

数据看起来像这样:

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

我需要遍历 IEnumerable 并通过检查日期和时间来检查数据。我想将同一天的事件分组在一起,并且事件的 EventStart 时间距之前的活动 EventEnd 时间不超过 30 分钟。

逻辑很难。我一直在尝试不同的事情,但我找不到任何方法。

这是我到目前为止所达到的地方:

// 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  :(
    }
}

全部完成后,它看起来像这样:

如果有人可以提供一些帮助,我将不胜感激。谢谢!

标签: c#linq

解决方案


这是一些 linq 方法,每天分组并在每天Aggregate创建动态子组(实际上是列表):

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

关键是,当条目小于X相距时对条目进行分组的要求不是一个合适的分组条件。因此,每天需要对(排序的)条目进行某种迭代。

我在这里并不特别提倡在传统循环上使用 Aggregate。这就是我决定编写这个东西的方式。编码循环可能对初学者更友好(更容易阅读)。


推荐阅读