C# 遍历列表以使用.NET添加元素
我有一个物品清单。每个对象都有一个整数和一个DateTime变量,该变量包含月份和年份值。我希望遍历列表并通过添加缺少的月份(数量为0)来填充列表,以便在列表中表示所有连续月份。实现这一目标的最佳方式是什么 例如: 原始列表 {Jan10,3},{Feb10,4},{Apr10,2},{May10,2},{Aug10,3},{Sep10,-3},{Oct10,6},{Nov10,3},{Dec10,7},{Feb11,3}C# 遍历列表以使用.NET添加元素,c#,linq,list,linq-to-objects,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} var months = new [] { "Jan", "Feb",
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 ...;
}
});
新名单
{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},{p>一个可能的算法是跟踪前几个月和当前几个月。如果“上一个”和“当前”之间的差值为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 ...;
}
});
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;
}
编译此文件所需的其他代码:
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 }
};
class-Foo
{
公共日期时间月{get;set;}
公共整数计数{get;set;}
}
列表月份=新列表
{
新Foo{Month=newdatetime(2010,1,1),Count=3},
新Foo{Month=newdatetime(2010,2,1),Count=4},
新Foo{Month=newdatetime(2010,4,1),Count=2},
新Foo{Month=newdatetime(2010,5,1),Count=2},
新Foo{Month=newdatetime(2010,8,1),Count=3},
新Foo{Month=newdatetime(2010,9,1),Count=-3},
新Foo{Month=newdatetime(2010,10,1),Count=6},
新Foo{Month=newdatetime(2010,11,1),Count=3},
新Foo{Month=newdatetime(2010,12,1),Count=7},
新Foo{Month=newdatetime(2011,2,1),Count=3}
};
注意:为简单起见,我没有处理原始列表为空的情况,但您应该在生产代码中执行此操作。让我们假设该结构作为
列表保存
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();
一种方法是实现对象的IEqualityComparer,然后可以创建一个“填充”对象列表,使用“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
}
公共类MyClass
{
public DateTime MonthYear{get;set;}
公共整数数量{get;set;}
}
公共类MyClassEqualityComparer:IEqualityComparer
{
#地区资格比较成员
公共布尔等于(MyClass x,MyClass y)
{
返回x.MonthYear==y.MonthYear;
}
公共int GetHashCode(MyClass obj)
{
返回obj.MonthYear.GetHashCode();
}
#端区
}
然后你可以这样做
// 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()));
//这是您真正的对象列表
List myClasses=新列表()
{
new MyClass(){MonthYear=new DateTime(2010,1,1),Quantity=3},
new MyClass(){MonthYear=new DateTime(2010,12,1),Quantity=2}
};
List fillerClasses=新列表();
对于(int i=1;i<12;i++)
{
MyClass filler=new MyClass(){Quantity=0,MonthYear=new DateTime(2010,i,1)};
填充类。添加(填充);
}
AddRange(fillerClasses.Exception(myClasses,new MyClassEqualityComparer());
如果我正确理解“日期时间”月份:
for(int i=0;i<12;i++)
如果(!original.Any(n=>n.datetimeproperty.Month==i))
Add(newmyclass{DateTimePropery=newdatetime(2010,i,1),IntQuantity=0});
var sorted=original.OrderBy(n=>n.datetimeproperty.Month);
考虑到年限、速度和可扩展性,它可以作为可枚举的扩展(甚至可能使用通用属性选择器)。
如果日期已经被截断到月份,并且列表在执行FILSET之前被命令,请考虑此方法:
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;
}
}
}
如何获得包含月份和年份的DateTime变量?该类有一个Date类型的字段/属性,该字段/属性管理不保存日期?或者,它是一个字符串(“Apr10”)字段/属性吗?这个列表应该是一天吗?@Addie如果您使用2010年1月
而不是2010年1月10日
,您的示例可能会更清楚,看起来有一群人认为这意味着1月10日
,尽管您在文本中指定了月份和年份,但此列表是动态的。DateTime对象唯一重要的部分是月份和年份。该列表通常包括多个年份,如1月10日、2月10日、1月11日等。我之前也按日期对列表进行了排序@Patrick DateTime对象将保存月份和年份以外的值,但我不关心它们,也不会访问这些值。我在Apr10中编写了一个伪代码,表示只有月份和年份的DateTime对象significant@JaredPar我不明白你的问题。你能澄清一下吗?列表是月/年DateTime对象以及一个表示数量的整数值的集合。他在同一个列表中有Jan10和Jan11。@Tanzelax,这看起来像一个打字错误,我将添加一条注释以确认Nah,他希望最终列表中有14个元素,从2010年1月到2011年2月,几乎每个发布回复的人都在他的例子中忽略了这一点p@Tanzelax. 正确,我特别需要考虑多年。哦,与我的解决方案类似:)这适用于规范,非常简单,性能也很好。实际上,即使它适用于规范,也不需要创建新的对象列表,因为您可以使用yield指令遍历有序列表并只返回新的缺少的对象(详情如下)
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