Linq 连续日期 takewhile

发布于 2024-08-09 08:58:06 字数 2740 浏览 2 评论 0原文

主要问题:如何按日期对元素进行分组,只有在连续且匹配某些属性的情况下?

详细信息

给定这种类型的对象:

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而不是方法比较字段

Main problem: how do i group elements by their Date, only if continuous and only if they match some properties?

Details:

Given this type of object:

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

I have a list of them, populated as follow:

//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},
};

Wanted (pseudocode):

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

Tests so far:

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

This is partially working, as of course it will return the correct results only for the first matching elements.

I could Skip(count) the list until 0 elements, but I would like to use group by, using an IEqualityComparer instead of method CompareFields.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

几味少女 2024-08-16 08:58:06

您也许可以使用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
    }
}

You may be able to use Enumerable.GroupBy. However, I have a feeling it might loop through the entire set. If so, you could always create your own SequencedGroupBy extension method.

Warning: won't work with parallel extensions (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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文