我有一个对象列表。每个对象都有一个整数和一个包含月份和年份值的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}
答案 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