将各个日期的集合展平为日期范围

时间:2010-07-02 08:44:46

标签: c# .net-3.5

给出单日日期(dd-mm-yyyy)的数据表(如下左图所示)。

将此转换为范围集合的最优雅方式是,为每种类型分组连续几天(在右侧显示)?

我们可以假设初始数据按TypeID排序,然后按日期排序。

TypeID  | Date            ->   TypeID  | Start      | End
1       | 01/02/2010           1       | 01/02/2010 | 03/02/2010
1       | 02/02/2010           2       | 03/02/2010 | 04/02/2010
1       | 03/02/2010           2       | 06/02/2010 | 06/02/2010
2       | 03/02/2010
2       | 04/02/2010
2       | 06/02/2010

我对LINQ并不特别,但我认为这可能是要走的路?

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

当然有人会提出一个简洁的LINQ解决方案,但这是我的旧式解决方案。当然它有问题,不会应付所有组合,拼凑在一起解决问题,但不以任何方式优雅: - (

internal class SourceData
{
    public int TypeId { get; set; }
    public DateTime Date { get; set; }
}

internal class Result
{
    public int TypeId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

class Program
{

    static void Main()
    {

        var a = new List<SourceData> {
            new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 01)},
            new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 02)},
            new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 03)}, 
            new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 03)}, 
            new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 04)}, 
            new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 06)} 
        };

        var results = new List<Result>();
        int currentTypeId = 1;
        var rangeEndDate = new DateTime();

        DateTime rangeStartDate = a[0].Date;
        DateTime currentDate = a[0].Date;

        for (int i = 1; i < a.Count() ; i++)
        {

            if (a[i].TypeId != currentTypeId)
            {
                results.Add(new Result() { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate });
                currentTypeId += 1;                    
                rangeStartDate = a[i].Date;
            }

            TimeSpan tSpan = a[i].Date - currentDate;
            int differenceInDays = tSpan.Days;

            if(differenceInDays > 1)
            {
                results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = a[i-1].Date });
                rangeStartDate = a[i].Date;
            }

            rangeEndDate = a[i].Date;
            currentDate = a[i].Date;
        }

        results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate });

        Console.WriteLine("Output\n");
        foreach (var r in results)
            Console.WriteLine( string.Format( "{0} - {1} - {2}",r.TypeId,r.StartDate.ToShortDateString(),r.EndDate.ToShortDateString()));

        Console.ReadLine();

    }
}

提供以下输出: -

输出

1 - 01/02/2010 - 03/02/2010

2 - 03/02/2010 - 04/02/2010

2 - 06/02/2010 - 06/02/2010

答案 1 :(得分:2)

注意以前的答案已删除。

编辑尝试修改后的这个:

public static IEnumerable<TAnonymous> Flatten<T, TAnonymous>(
    this IEnumerable<T> enumerable,
    Func<T, T, bool> criteria,
    Func<T, T, TAnonymous> selector,
    Func<TAnonymous, T, T, TAnonymous> modifier)
{
    var list = new List<TAnonymous>();

    T last = default(T);
    bool first = true;
    bool created = false;

    TAnonymous current = default(TAnonymous);

    Action<T, T> action = (a, b) =>
                          {
                              if (criteria(a, b)) {
                                  if (created) {
                                      current = modifier(current, a, b);
                                  } else {
                                      current = selector(a, b);
                                      created = true;
                                  }
                              } else {
                                  if (created) {
                                      list.Add(current);
                                      current = default(TAnonymous);
                                      created = false;
                                  } else {
                                      list.Add(selector(a, a));
                                  }
                              }
                          };

    foreach (T item in enumerable) {
        if (first) {
            first = false;
            last = item;
            continue;
        }

        action(last, item);
        last = item;
    }

    action(last, last);

    if (created)
        list.Add(current);

    return list;
}

被称为:

var filtered = list.Flatten(
    (r1, r2) => ((r2.Date - r1.Date).Days <= 1 && r1.TypeID == r2.TypeID),
    (r1, r2) => new { r1.TypeID, Start = r1.Date, End = r2.Date },
    (anon, r1, r2) => new { anon.TypeID, anon.Start, End = r2.Date });

应该有希望工作......这次我们正在做的是将操作分成几个阶段,首先匹配一个标准,然后创建一个新项目(选择器),或更新以前创建的项目(修饰符)。