代码之家  ›  专栏  ›  技术社区  ›  Alex Bagnolini

Linq连续日期TakeWhile

  •  0
  • Alex Bagnolini  · 技术社区  · 15 年前

    主要问题:我如何按元素的日期对它们进行分组,只有当元素是连续的,并且只有当它们与某些属性匹配时才进行分组?

    细节 :

    给定此类型的对象:

    public class MyObj
    {
        public DateTime Date { get; set; }
        public string ConditionalValue1 { get; set; }
        public int ConditionalValue2 { get; set; }
        public int OtherValue { get; set; }
    
        //for test purposes only, i would like to avoid doing this
        public bool CompareFields(MyObj toBeCompared)
        {
            return ConditionalValue1 == toBeCompared.ConditionalValue1 &&
                   ConditionalValue2 == toBeCompared.ConditionalValue2; 
        }
    }
    

    我有它们的列表,填充如下:

    //list is ordered for semplicity, but it won't be by default
    //list is a result of a group by too
    var myList = new[]
    {
        new MyObj { Date = new DateTime(2009, 10, 20), ConditionalValue1 = "A", ConditionalValue2 = 1, OtherValue = 1 },
        new MyObj { Date = new DateTime(2009, 10, 21), ConditionalValue1 = "A", ConditionalValue2 = 1, OtherValue = 2 },
        new MyObj { Date = new DateTime(2009, 10, 22), ConditionalValue1 = "B", ConditionalValue2 = 1, OtherValue = 3 },
        new MyObj { Date = new DateTime(2009, 10, 23), ConditionalValue1 = "A", ConditionalValue2 = 2, OtherValue = 4 },
        new MyObj { Date = new DateTime(2009, 10, 24), ConditionalValue1 = "A", ConditionalValue2 = 2, OtherValue = 5 },
        new MyObj { Date = new DateTime(2009, 10, 25), ConditionalValue1 = "A", ConditionalValue2 = 1, OtherValue = 6 },
        //Note the hole for day 26
        new MyObj { Date = new DateTime(2009, 10, 27), ConditionalValue1 = "A", ConditionalValue2 = 1, OtherValue = 7},
    };
    

    需要的 (伪代码):

    List = 
      { ListOfObjects }, //First has date = "2009/10/20", last has = "2009/10/21" 
      { ListOfObjects }, //First has date = "2009/10/22", last has = "2009/10/22" //reason: doesn't match Value1
      { ListOfObjects }, //First has date = "2009/10/23", last has = "2009/10/24" 
      { ListOfObjects }, //First has date = "2009/10/25", last has = "2009/10/25" //reason: doesn't match Value2
      { ListOfObjects }  //First has date = "2009/10/27", last has = "2009/10/27" //reason: there is no "2009/10/26" object
    

    迄今为止的测试 以下内容:

    //i pick the first element, to be compared with the next ones
    var firstInList = myList.First();
    //take while date is consequent and fields are compared
    var partialList = myList
        .TakeWhile(
            (obj, index) => obj.Date == firstInList.Date.AddDays(index) &&
                            obj.CompareFields(firstInList)
        );
    

    这是部分工作,因为它当然只会返回第一个匹配元素的正确结果。

    我可以 Skip(count) 列表,直到0个元素,但我要使用 group by ,使用 IEqualityComparer 代替方法 CompareFields .

    1 回复  |  直到 15 年前
        1
  •  2
  •   Richard Szalay    15 年前

    你可能会使用 Enumerable.GroupBy .不过,我觉得它可能会在整个场景中循环。如果是这样,您可以始终创建自己的SequencedGroupBy扩展方法。

    警告: 无法使用并行扩展(Asparallel)

    DateTime lastGroupDate = DateTime.MinValue;
    DateTime lastDate = DateTime.MinValue;
    
    IEnumerable<IGrouping<DateTime, MyObj>> groups = myList
        .GroupBy(o =>
            {
                if (lastGroupDate != DateTime.MinValue &&
                    o.Date.AddDays(-1) == lastDate)
                {
                    lastDate = o.Date;
                }
                else
                {
                    lastGroupDate = o.Date;
                    lastDate = o.Date;
                }
    
                return lastGroupDate;
            }
        );
    
    foreach(IGrouping<DateTime, MyObj> grouping in groups)
    {
        // grouping.Key == the grouped date / first in sequence
    
        foreach(MyObj obj in grouping)
        {
            // obj.Date == actual date
        }
    }