在C#列表的下一个列表项中基于StartDate设置EndDate

时间:2016-08-16 15:17:03

标签: c# linq

我有一个C#列表项目如下 -

List<MyClass> All_Items = GetListItems();

GetListItems()返回结果如下 -

Category    StartDate    EndDate
AA          2008-05-1    
AA          2012-02-1
BB          2009-09-1
BB          2010-08-1
CC          2009-10-1

All_Items上使用LINQ,我希望以如下方式更新EndDate列

  1. 如果当前类别的StartDate小于同一类别中下一个较大日期项目的开始日期,则使用的日期少于较大日期的日期。
  2. 如果没有更大的日期,请更新至2099-12-31
  3. 最终结果如下 -

    Category    StartDate    EndDate
    AA          2008-05-1    2012-01-31
    AA          2012-02-1    2099-12-31
    BB          2009-09-1    2010-07-31
    BB          2010-08-1    2099-12-31
    CC          2009-10-1    2099-12-31
    

    我只能想到使用太多循环来完成它。什么是更好的选择?

5 个答案:

答案 0 :(得分:2)

您可以为每个类别选择日期并将其放入字典中以便以后节省时间。

然后,根据您的要求,您只需检查所有项目并检查开始日期是否小于下一类。

这是:

        var categoryDictionary = All_Items
                                    .GroupBy(i => i.Category)
                                    .ToDictionary(
                                        g => g.Key, 
                                        g => g.Select(i => i.StartDate));
        var defaultDate = DateTime.Parse("2099-12-31");
        foreach (var item in All_Items)
        {
            var nextDateInCategory = categoryDictionary[item.Category]
                                        .Where(i => i > item.StartDate)
                                        .OrderBy(i => i)
                                        .FirstOrDefault();
            item.EndDate =
                nextDateInCategory != default(DateTime)
                    ? nextDateInCategory.AddDays(-1) 
                    : defaultDate;
        }

答案 1 :(得分:2)

试试这段代码。它遍历所有项目并选择下一个更大的项目.StartDate为同一类别。 如果此类项目不可用,则会设置默认日期。

我无法测试代码,因为我正在手机上书写,因此欢迎任何更正。

foreach(var item in All_Items)
{
    var nextItem = (from i in All_Items 
                          where i != null && 
                                      i.Category == item.Category &&
                                      i.StartDate > item.StartDate
                          orderby i.StartDate
                          select i).FirstOrDefault();
   item.EndDate = nextItem != null ? nextItem.StartDate.AddDays(-1) : new DateTime(2099,12,31);
}

答案 2 :(得分:2)

LINQ不适合处理序列元素之间的依赖关系,并且肯定不用于更新。

以下是实现目标的简单有效方法:

var groups = All_Items.OrderBy(item => item.StartDate).GroupBy(item => item.Category);
foreach (var group in groups)
{
    MyClass last = null;
    foreach (var item in group)
    {
        if (last != null) last.EndDate = item.StartDate.AddDays(-1);
        last = item;
    }
    last.EndDate = new DateTime(2099, 12, 31);
}

因此我们使用LINQ来按StartDate对元素进行排序,并将结果分组为Category(这样可以保留每个组内的排序)。然后简单地迭代LINQ查询结果并相应地更新EndDate

答案 3 :(得分:1)

让我们假设您的MyClass看起来像这样:

public class MyClass
{
    public string Category { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

以下是如何操作的方法,请参阅代码中的注释以获得解释。

IEnumerable<MyClass> All_Items = new List<MyClass>
{
    new MyClass { Category = "AA", StartDate = new DateTime(2008, 5, 1) },
    new MyClass { Category = "AA", StartDate = new DateTime(2012, 2, 1) },
    new MyClass { Category = "BB", StartDate = new DateTime(2009, 9, 1) },
    new MyClass { Category = "BB", StartDate = new DateTime(2010, 8, 1) },
    new MyClass { Category = "CC", StartDate = new DateTime(2009, 10, 1) }
}
    // Group by category
    .GroupBy(c => c.Category)
    // Colapse the groups into a single IEnumerable
    .SelectMany(g => 
    {
        // Store the already used dates
        List<DateTime> usedDates = new List<DateTime>();

        // Get a new MyClass that has the EndDate set, from each MyClass in the category
        return g.Select(c => 
        {
            // Get all biggerDates that were not used already
            var biggerDates = g.Where(gc => gc.StartDate > c.StartDate && !usedDates.Any(ud => ud == gc.StartDate));
            // Set the endDate to the default one
            DateTime date = new DateTime(2099, 12, 31);

            // If a bigger date was found, mark it as used and set the EndDate to it
            if (biggerDates.Any()) {
                date = biggerDates.Min(gc => gc.StartDate).AddDays(-1);
                usedDates.Add(date);
            }

            return new MyClass
            {
                Category = c.Category,
                StartDate = c.StartDate,
                EndDate = date
            };
        });
    });

答案 4 :(得分:1)

在单个LINQ语句中(maxEndDate为2099-12-31):

All_Items.GroupBy(category => category.Category).Select(key =>
                {
                    var maxCategoryStartDate = key.Max(value => value.StartDate);
                    return key.Select(v => {
                        if (DateTime.Equals(v.StartDate, maxCategoryStartDate))
                        {
                            v.EndDate = maxEndDate;
                        }
                        else
                        {
                            v.EndDate = maxCategoryStartDate - TimeSpan.FromDays(1);
                        }
                        return v;
                        });
                }
            ).SelectMany(x => x);