遍历列表以使用.NET添加元素

时间:2010-03-23 19:51:00

标签: c# linq list linq-to-objects

我有一个对象列表。每个对象都有一个整数和一个包含月份和年份值的DateTime变量。我想遍历列表并通过添加缺少的月份(数量为0)填充列表,以便在列表中表示所有连续月份。什么是实现这一目标的最佳方式?

实施例: 原始清单

{Jan10,3},{Feb10,4},{Apr10,2},{May10,2},{Aug10,3},{Sep10,-3},{Oct10,6},{Nov10,3 },{Dec10,7},{Feb11,3}

新列表

{Jan10,3},{Feb10,4}, {Mar10,0}} ,{Apr10,2},{May10,2}, {Jun10,0},{ Jul10,0} {Aug10,3},{Sep10,-3},{Oct10,6},{Nov10,3},{Dec10,7}, {Jan11,0}} ,{Feb11,3}

6 个答案:

答案 0 :(得分:3)

一种可能的算法是跟踪前几个月和当前月份。如果先前和当前之间的差异是1个月,则将结果附加到当前。如果差异超过一个月,请先删除缺失的月份,然后再复制当月。

Foo prev = months.First();
List<Foo> result = new List<Foo> { prev };
foreach (Foo foo in months.Skip(1))
{
    DateTime month = prev.Month;
    while (true)
    {
        month = month.AddMonths(1);
        if (month >= foo.Month)
        {
            break;
        }
        result.Add(new Foo { Month = month, Count = 0 });
    }
    result.Add(foo);
    prev = foo;
}

结果:

01-01-2010 00:00:00: 3
01-02-2010 00:00:00: 4
01-03-2010 00:00:00: 0
01-04-2010 00:00:00: 2
01-05-2010 00:00:00: 2
01-06-2010 00:00:00: 0
01-07-2010 00:00:00: 0
01-08-2010 00:00:00: 3
01-09-2010 00:00:00: -3
01-10-2010 00:00:00: 6
01-11-2010 00:00:00: 3
01-12-2010 00:00:00: 7
01-01-2011 00:00:00: 0
01-02-2011 00:00:00: 3

进行此编译所需的其他代码:

class Foo
{
    public DateTime Month { get; set; }
    public int Count { get; set; }
}

List<Foo> months = new List<Foo>
{
    new Foo{ Month = new DateTime(2010, 1, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 2, 1), Count = 4 },
    new Foo{ Month = new DateTime(2010, 4, 1), Count = 2 },
    new Foo{ Month = new DateTime(2010, 5, 1), Count = 2 },
    new Foo{ Month = new DateTime(2010, 8, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 9, 1), Count = -3 },
    new Foo{ Month = new DateTime(2010, 10, 1), Count = 6 },
    new Foo{ Month = new DateTime(2010, 11, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 12, 1), Count = 7 },
    new Foo{ Month = new DateTime(2011, 2, 1), Count = 3 }
};

注意:为简单起见,我没有处理原始列表为空的情况,但您应该在生产代码中执行此操作。

答案 1 :(得分:3)

让我们假设结构保持为List<Tuple<DateTime,int>>

var oldList = GetTheStartList();
var map = oldList.ToDictionary(x => x.Item1.Month);

// Create an entry with 0 for every month 1-12 in this year 
// and reduce it to just the months which don't already 
// exist 
var missing = 
  Enumerable.Range(1,12)
  .Where(x => !map.ContainsKey(x))
  .Select(x => Tuple.Create(new DateTime(2010, x,0),0))

// Combine the missing list with the original list, sort by
// month 
var all = 
  oldList
  .Concat(missing)
  .OrderBy(x => x.Item1.Month)
  .ToList();

答案 2 :(得分:1)

var months = new [] { "Jan", "Feb", "Mar", ... };
var yourList = ...;
var result = months.Select(x => {
  var yourEntry = yourList.SingleOrDefault(y => y.Month = x);
  if (yourEntry != null) {
    return yourEntry;
  } else {
    return new ...;
  }
});

答案 3 :(得分:0)

一种方法是实现IEqualityComparer&lt;&gt;对于您的对象,您可以使用“Except”扩展方法创建要添加到现有列表的“填充”对象列表。有点像下面

public class MyClass
{
    public DateTime MonthYear { get; set; }
    public int Quantity { get; set; }
}

public class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
    #region IEqualityComparer<MyClass> Members

    public bool Equals(MyClass x, MyClass y)
    {
        return x.MonthYear == y.MonthYear;
    }

    public int GetHashCode(MyClass obj)
    {
        return obj.MonthYear.GetHashCode();
    }

    #endregion
}

然后你可以做这样的事情

// let this be your real list of objects    
List<MyClass> myClasses = new List<MyClass>() 
{
    new MyClass () { MonthYear = new DateTime (2010,1,1), Quantity = 3},
    new MyClass() { MonthYear = new DateTime (2010,12,1), Quantity = 2}
};

List<MyClass> fillerClasses = new List<MyClass>();
for (int i = 1; i < 12; i++)
{
    MyClass filler = new MyClass() { Quantity = 0, MonthYear = new DateTime(2010, i, 1) };
    fillerClasses.Add(filler);
}

myClasses.AddRange(fillerClasses.Except(myClasses, new MyClassEqualityComparer()));

答案 4 :(得分:0)

如果我对“DateTime”月份理解正确:

    for (int i = 0; i < 12; i++)
        if (!original.Any(n => n.DateTimePropery.Month == i))
            original.Add(new MyClass {DateTimePropery = new DateTime(2010, i, 1), IntQuantity = 0});
    var sorted = original.OrderBy(n => n.DateTimePropery.Month);

答案 5 :(得分:0)

考虑到年份,速度和可扩展性,它可以作为可枚举的扩展(甚至可能使用通用属性选择器)来完成。 如果日期已被截断为月份并且在执行FillMissing之前订购了列表,请考虑以下方法:

public static class Extensions
{
    public static IEnumerable<Tuple<DateTime, int>> FillMissing(this IEnumerable<Tuple<DateTime, int>> list)
    {
        if(list.Count() == 0)
            yield break;
        DateTime lastDate = list.First().Item1;
        foreach(var tuple in list)
        {
            lastDate = lastDate.AddMonths(1);
            while(lastDate < tuple.Item1)
            {
                yield return new Tuple<DateTime, int>(lastDate, 0);
                lastDate = lastDate.AddMonths(1);
            }
            yield return tuple;
            lastDate = tuple.Item1;
        }
    }
}

并以示例形式:

    private List<Tuple<DateTime, int>> items = new List<Tuple<DateTime, int>>()
    {
        new Tuple<DateTime, int>(new DateTime(2010, 1, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 2, 1), 4),
        new Tuple<DateTime, int>(new DateTime(2010, 4, 1), 2),
        new Tuple<DateTime, int>(new DateTime(2010, 5, 1), 2),
        new Tuple<DateTime, int>(new DateTime(2010, 8, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 9, 1), -3),
        new Tuple<DateTime, int>(new DateTime(2010, 10, 1), 6),
        new Tuple<DateTime, int>(new DateTime(2010, 11, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 12, 1), 7),
        new Tuple<DateTime, int>(new DateTime(2011, 2, 1), 3)
    };

    public Form1()
    {
        InitializeComponent();
        var list = items.FillMissing();
        foreach(var element in list)
        {
            textBox1.Text += Environment.NewLine + element.Item1.ToString() + " - " + element.Item2.ToString();
        }
    }

将导致文本框包含:

2010-01-01 00:00:00 - 3
2010-02-01 00:00:00 - 4
2010-03-01 00:00:00 - 0
2010-04-01 00:00:00 - 2
2010-05-01 00:00:00 - 2
2010-06-01 00:00:00 - 0
2010-07-01 00:00:00 - 0
2010-08-01 00:00:00 - 3
2010-09-01 00:00:00 - -3
2010-10-01 00:00:00 - 6
2010-11-01 00:00:00 - 3
2010-12-01 00:00:00 - 7
2011-01-01 00:00:00 - 0
2011-02-01 00:00:00 - 3