使用LINQ按日期对序列进行分组,没有间隔 [英] Use LINQ to group a sequence by date with no gaps

查看:160
本文介绍了使用LINQ按日期对序列进行分组,没有间隔的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试选择一个列表的子组,其中项目具有连续的日期,例如

I'm trying to select a subgroup of a list where items have contiguous dates, e.g.


ID  StaffID  Title              ActivityDate
--  -------  -----------------  ------------
 1       41  Meeting with John    03/06/2010
 2       41  Meeting with John    08/06/2010
 3       41  Meeting Continues    09/06/2010
 4       41  Meeting Continues    10/06/2010
 5       41  Meeting with Kay     14/06/2010
 6       41  Meeting Continues    15/06/2010

我每次都使用一个枢轴点,因此将示例枢轴项设为3,我希望围绕枢轴获得以下结果连续事件:

I'm using a pivot point each time, so take the example pivot item as 3, I'd like to get the following resulting contiguous events around the pivot:


ID  StaffID  Title              ActivityDate
--  -------  -----------------  ------------
 2       41  Meeting with John    08/06/2010
 3       41  Meeting Continues    09/06/2010
 4       41  Meeting Continues    10/06/2010

我当前的实现方式是费力地走"过去,然后走到未来,以建立列表:

My current implementation is a laborious "walk" into the past, then into the future, to build the list:

var activity = // item number 3: Meeting Continues (09/06/2010)

var orderedEvents = activities.OrderBy(a => a.ActivityDate).ToArray();

// Walk into the past until a gap is found
var preceedingEvents = orderedEvents.TakeWhile(a => a.ID != activity.ID);
DateTime dayBefore;
var previousEvent = activity;
while (previousEvent != null)
{
    dayBefore = previousEvent.ActivityDate.AddDays(-1).Date;
    previousEvent = preceedingEvents.TakeWhile(a => a.ID != previousEvent.ID).LastOrDefault();
    if (previousEvent != null)
    {
        if (previousEvent.ActivityDate.Date == dayBefore)
            relatedActivities.Insert(0, previousEvent);
        else
            previousEvent = null;
    }
}


// Walk into the future until a gap is found
var followingEvents = orderedEvents.SkipWhile(a => a.ID != activity.ID);
DateTime dayAfter;
var nextEvent = activity;
while (nextEvent != null)
{
    dayAfter = nextEvent.ActivityDate.AddDays(1).Date;
    nextEvent = followingEvents.SkipWhile(a => a.ID != nextEvent.ID).Skip(1).FirstOrDefault();
    if (nextEvent != null)
    {
        if (nextEvent.ActivityDate.Date == dayAfter)
            relatedActivities.Add(nextEvent);
        else
            nextEvent = null;
    }
}

列表relatedActivities然后应按顺序包含连续事件.

The list relatedActivities should then contain the contiguous events, in order.

是否有更好的方法(也许使用LINQ)?

我想到了使用 .Aggregate() ,但是当它在序列中发现缺口时,却想不出如何使聚合爆发.

I had an idea of using .Aggregate() but couldn't think how to get the aggregate to break out when it finds a gap in the sequence.

推荐答案

在这种情况下,我认为标准的foreach循环可能比LINQ查询更具可读性:

In this case I think that a standard foreach loop is probably more readable than a LINQ query:

var relatedActivities = new List<TActivity>();
bool found = false;

foreach (var item in activities.OrderBy(a => a.ActivityDate))
{
    int count = relatedActivities.Count;
    if ((count > 0) && (relatedActivities[count - 1].ActivityDate.Date.AddDays(1) != item.ActivityDate.Date))
    {
        if (found)
            break;

        relatedActivities.Clear();
    }

    relatedActivities.Add(item);
    if (item.ID == activity.ID)
        found = true;
}

if (!found)
    relatedActivities.Clear();

对于它的价值,这是一个大致等效的LINQ查询,而且可读性差得多:

For what it's worth, here's a roughly equivalent -- and far less readable -- LINQ query:

var relatedActivities = activities
    .OrderBy(x => x.ActivityDate)
    .Aggregate
    (
        new { List = new List<TActivity>(), Found = false, ShortCircuit = false },
        (a, x) =>
        {
            if (a.ShortCircuit)
                return a;

            int count = a.List.Count;
            if ((count > 0) && (a.List[count - 1].ActivityDate.Date.AddDays(1) != x.ActivityDate.Date))
            {
                if (a.Found)
                    return new { a.List, a.Found, ShortCircuit = true };

                a.List.Clear();
            }

            a.List.Add(x);
            return new { a.List, Found = a.Found || (x.ID == activity.ID), a.ShortCircuit };
        },
        a => a.Found ? a.List : new List<TActivity>()
    );

这篇关于使用LINQ按日期对序列进行分组,没有间隔的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆