A trace list for adding items using .NET.

I have a list of objects. Each object has an integer and a DateTime variable that contains the value of the month and year. I would like to go through the list and fill out the list, adding the missing months (with the quantity 0) so that all consecutive months are presented in the list. What would be the best way to achieve this?

Example: Source List

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

New list

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

+4
source share
6 answers

One possible algorithm is to track previous and current months. If the difference between the previous and the current is 1 month, add current to the result. If the difference is more than one month, first add the missing months, and then copy the current month.

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

Results:

 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 

Other code needed to compile:

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

Note. For simplicity, I did not handle the case where the source list is empty, but you must do this in production code.

+3
source

Suppose the structure is held as 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(); 
+3
source
 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 ...; } }); 
+1
source

One way is to implement the IEqualityComparer <> object of your object, then you can create a list of filler objects to add to the existing list using the Exclude extension method. As below

 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 } 

And then you can do something like this

 // 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())); 
0
source

If I understand the month "DateTime" correctly:

  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); 
0
source

Given years, speed, and extensibility, this can be done as an enumerated extension (possibly even using a universal property selector). If dates have already been truncated to a month and the list is ordered before FillMissing is executed, consider this method:

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

and in the form of an example:

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

which will result in a text box containing:

 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 
0
source

Source: https://habr.com/ru/post/1304927/


All Articles