代码之家  ›  专栏  ›  技术社区  ›  RYFN

将单个日期集合展平为日期范围

  •  1
  • RYFN  · 技术社区  · 14 年前

    给出了一个单日日期(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
    

    我不太清楚我的问题,但我在想那可能是我该走的路?

    如有任何帮助,我们将不胜感激!

    2 回复  |  直到 14 年前
        1
  •  2
  •   Andy Robinson    14 年前

    当然有人会想出一个简洁的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
  •  2
  •   Matthew Abbott    14 年前

    注意 先前的答案已删除。

    编辑

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

    希望能奏效。。。我们这次要做的是将操作分解为几个阶段,首先匹配一个条件,然后创建一个新项(选择器),或者更新先前创建的项(修饰符)。