How to diff two lists and get net changes - c#

I have an object and two lists as follows:
public class MyObject
{
public int Key;
public DateTime Day;
public decimal Value;
}
List<MyObject> listA = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 }
};
List<MyObject> listB = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
The results I am looking for are:
List<MyObject> listChanges = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = -6 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = -8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
Essentially, I am trying to create a list which contains the changes that would be required to make listA into listB. Thus, while Except, and Intersect from LINQ could be used I don't think they will have the best performance to do such a task as you would still need another comparison to get the difference in the values.
One thought I had is: If I loop through listA, I can remove the item from listA and from listB (if it is found and at this time I can determine the +/- differences). Once I have finished with listA, listB would only contain additions.
How can I get the change results?

This should do it. It'll throw an exception if any of your Key/Day combinations are not unique within either of your inputs.
public static IEnumerable<MyObject> GetChanges(
IEnumerable<MyObject> from, IEnumerable<MyObject> to)
{
var dict = to.ToDictionary(mo => new { mo.Key, mo.Day });
// Check that keys are distinct in from, too:
var throwaway = from.ToDictionary(mo => new { mo.Key, mo.Day });
// Adjustments of items found in "from"
foreach (MyObject mo in from)
{
var key = new { mo.Key, mo.Day };
MyObject newVal;
if (dict.TryGetValue(key, out newVal))
{
// Return item indicating adjustment
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = newVal.Value - mo.Value };
dict.Remove(key);
}
else
{
// Return item indicating removal
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = -mo.Value };
}
}
// Creation of new items found in "to"
foreach (MyObject mo in dict.Values)
{
// Return item indicating addition
// (Clone as all our other yields are new objects)
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = mo.Value };
}
}
You could speed this up by removing the uniqueness check on from or doing it on-the-fly (try adding each item's key-parts to a HashSet), but I don't think you can avoid looping over part of to twice - once to build the dictionary, once to return the remainder.

First I would implement an IEqualityComparer<T> that checks equality based on both the Key and Day properties. Then you could use linq as follows:
var notInA = listB.Except(listA, myEqualityComparer);
var notInB = listA.Except(listB, myEqualityComparer)
.Select(o => {
return new MyObject {
Key = item.Key,
Day = item.Day,
Value = item.Value * -1
};
});
var listA2 = listA.Intersect(listB, myEqualityComparer)
.OrderBy(o => o.Key)
.ThenBy(o => o.Day);
var listB2 = listB.Intersect(listA, myEqualityComparer)
.OrderBy(o => o.Key)
.ThenBy(o => o.Day);
var diff = listA2.Zip(listB2, (first,second) => {
return new MyObject {
Key = first.Key,
Day = first.Day,
Value = second.Value - first.Value
});
diff = diff.Concat(notInA).Concat(notInB);

how about this :
List<MyObject> listA = new List<MyObject>(){
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 }
};
List<MyObject> listB = new List<MyObject>(){
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
List<MyObject> listChanges = Comparer(listA, listB);
MyObject[] hasil = listChanges.ToArray();
for (int a = 0; a < hasil.Length;a++ ) {
Console.WriteLine(hasil[a].Key+" "+hasil[a].Day+" "+hasil[a].Value);
}
and the function :
private MyObject[] sort(List<MyObject> input) {
//sort input with it's key
MyObject[] gg = input.ToArray();
for (int a = 0; a < input.Count; a++) {
for (int b = a + 1; b < input.Count; b++) {
if (gg[a].Key > gg[b].Key) {
MyObject temp = gg[a];
gg[a] = gg[b];
gg[b] = temp;
}
}
}
//sort input, if key is same => sort the date
for (int a = 0; a < input.Count; a++) {
int indStart = a;
int indEnd = a;
for (int b = a + 1; b < input.Count; b++) {
if (gg[a].Key == gg[b].Key) {
indEnd++;
} else {
b = input.Count;
}
}
a = indEnd;
for (int c = indStart; c <= indEnd; c++) {
for (int d = c + 1; d <= indEnd; d++) {
if (gg[c].Day > gg[d].Day) {
MyObject temp = gg[c];
gg[c] = gg[d];
gg[d] = temp;
}
}
}
}
return gg;
}
private List<MyObject> Comparer(List<MyObject> listA, List<MyObject> listB) {
List<MyObject> output = new List<MyObject>();
//if you sure that the list was sorted, u just remove the sort function
MyObject[] ff = sort(listA);
MyObject[] gg = sort(listB);
Boolean[] masuk = new Boolean[gg.Length];
//foreach element in listA, search the changes in input
for (int a = 0; a < listA.Count;a++ ) {
//find element in input which contains the changes of element in listA
Boolean ins = false;
for (int b = 0; b < listB.Count;b++ ) {
if (masuk[b])
continue;
if (ff[a].Key >= gg[b].Key) {
if (ff[a].Key == gg[b].Key && ff[a].Day == gg[b].Day){
masuk[b] = true;
if (gg[b].Value != ff[a].Value) {
output.Add(new MyObject() { Key = gg[b].Key, Day = gg[b].Day, Value = gg[b].Value - ff[a].Value });
b = listB.Count;
}
ins = true;
}
} else {
b = listB.Count;
}
}
if (!ins) {
output.Add(new MyObject() { Key = ff[a].Key, Day = ff[a].Day, Value = -ff[a].Value });
}
}
for (int a = 0; a < gg.Length;a++ ) {
if(!masuk[a]){
output.Add(new MyObject() { Key = gg[a].Key, Day = gg[a].Day, Value = gg[a].Value });
}
}
return output;
}
and the output :
1 12/17/2012 12:00:00 AM -6
2 12/17/2012 12:00:00 AM -8
3 12/17/2012 12:00:00 AM 4
5 12/17/2012 12:00:00 AM 10

Related

Splitting a list of objects based on a property c#

myClass structure :
public class myClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime ActivityTime { get; set; }
}
I have a list of the above class List<myClass> all ordered by ActivityTime.
I need to split the above list and get a List<List<myClass>> such that if there is a difference of more than a specific period say 5 mins between two consecutive ActivityTime I wish the split to take place.
Any help is sincerely appreciated.
Thanks
You can do that in a simple iteration:
var myList = new List<myClass>()
{
new myClass() { Name = "ABC", AdditionalData = "1", ActivityTime = DateTime.Now },
new myClass() { Name = "ABC2", AdditionalData = "2", ActivityTime = DateTime.Now },
new myClass() { Name = "ABC3", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(6) },
new myClass() { Name = "ABC4", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(11) },
new myClass() { Name = "ABC4", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(12) }
};
var results = new List<List<myClass>>();
myClass previousItem = null;
List<myClass> currentList = new List<myClass>();
foreach (var item in myList)
{
if (previousItem == null || (item.ActivityTime - previousItem.ActivityTime).TotalSeconds >= 5)
{
currentList = new List<myClass>();
results.Add(currentList);
}
currentList.Add(item);
previousItem = item;
}
What about this solution:
var data = new List<myClass> {
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 00, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 05, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 06, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 07, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 17, 00) }
};
var period = 5;
var firstActivityTime = data.Min(x => x.ActivityTime);
var answer = data.OrderBy(x => x.ActivityTime).GroupBy(x => {
var dif = (x.ActivityTime - firstActivityTime).Minutes;
return dif / period - (dif % period == 0 && dif / period != 0 ? 1 : 0);
}).Select(x => x.ToList()).ToList();
Various approaches this can be achieved, but underlying principle is same. Keep track of n-1(th) element when processing n(th) element and calculate timespan between these two.
You could do something like this.
List<MyClass> data = ...; // input
int gid=0;
DateTime prevvalue = data[0].ActivityTime; // Initial value
var result = data.Select(x=>
{
var obj = x.ActivityTime.Subtract(prevvalue).TotalMinutes<5? // Look for timespan difference
new {gid= gid, item =x} // Create groups based on consecutive gaps.
:new {gid= ++gid, item =x};
prevvalue= x.ActivityTime; // Keep track of previous value (for next element process)
return obj;
})
.GroupBy(x=>x.gid) // Split into groups
.Select(x=>x.Select(s=>s.item).ToList())
.ToList();
Check this Demo

List with dates, addHours to list linq

I have a List with dates to show it in a grid and I would like add one hour to range2:
private void grid_loaded(object sender, RoutedEventArgs e)
{
var data = new List<EntityViewModel>
{
new EntityViewModel { aDay = new DateTime(1980, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1983, 1, 1, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1985, 6, 14, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1990, 7, 3, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1995, 8, 1, 4, 23, 12) },
new EntityViewModel { aDay = new DateTime(1996, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(2000, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = DateTime.Now }
};
var range = data.Where(i => i.aDay.Year >= 1983 && i.aDay.Year <= 1996).ToList();
var range2 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
}
I tried this:
var range3 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
range3.ForEach(i => i.aDay.AddHours(1));
this:
foreach (var item in range2.Where(x => x.aDay != null))
{
item.aDay.AddHours(1);
}
and this:
var range5 = range2.Where(i => i.aDay != null).Select(i => { i.aDay.AddHours(1); return i; }).ToList();
But it doesn't do anything, are always the same hour.
AddHours does NOT alter the initial date, but return a new one.
You need to select these dates:
var range5 = range2.Where(i => i.aDay != null).Select(i => i.aDay.AddHours(1));
To get the altered dates or
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
To alter the dates of the items in the list.
DateTime is a structure, so you have to do:
var date = DateTime.Now;
date = date.AddHours(1);
in your case:
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
AddHours returns a new DateTime, it does not change the current instance.
http://msdn.microsoft.com/en-us/library/system.datetime.addhours.aspx
for (int i = 0; i < range2.Count; ++i) {
range2[i].aDay = range2[i].aDay.AddHours(1);
}

What is the best way to intersect these two lists?

I have 2 lists in C#:
public class AvailableSlot
{
public DateTime DateTime;
public string Name
}
List<AvailableSlot> list1 = GetList();
List<AvailableSlot> list2 = GetAnotherList();
I want to call intersect on these lists to find out where there are items in both lists for the same date. I know i can use .Intersect to get this info but I have a slightly more complicated requirement. I want to return a intersected list but i want to this list to contain a list of objects with all of the name in them. so something like this:
List<AvailableSlot2> intersectedList . ..
where AvailableSlot2 is this below:
public class AvailableSlot2
{
public DateTime DateTime;
public string[] Names;
}
Is there any ability to do this transformation after trying to intersect between two lists?
I would just union the two lists, group by DateTime and then pull out the names from the group:
var list1 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Alpha" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bravo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Charlie" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Delta" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Echo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Foxtrot" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 4), Name = "Golf" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hotel" }
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Apple" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bannana" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Dog" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Egg" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hi" }
};
var list3 = list1.Where (l => list2.Where (li => l.DateTime == li.DateTime).Any ())
.Union(list2.Where (l => list1.Where (li => l.DateTime == li.DateTime).Any ()));
var groupedItems = from slot in list3
group slot by slot.DateTime into grp
select new AvailableSlot2 {
DateTime = grp.Key,
Names = grp.Select (g => g.Name).ToArray()
};
foreach(var g in groupedItems)
{
Console.WriteLine(g.DateTime);
foreach(var name in g.Names)
Console.WriteLine(name);
Console.WriteLine("---------------------");
}
Output:
2/1/2013 12:00:00 AM
Alpha
Delta
Apple
Dog
---------------------
2/2/2013 12:00:00 AM
Bravo
Echo
Bannana
Egg
---------------------
2/5/2013 12:00:00 AM
Hotel
Hi
---------------------
You can use a LINQ to Objects Join() to line up items with the same DateTime property and then collect all the names into an array
var joinedItems = from slot1 in list1
join slot2 in list2
on slot1.DateTime equals slot2.DateTime into g
where g.Any()
select new AvailableSlot2
{
DateTime = slot1.DateTime,
Names = Enumerable.Range(slot1.Name,1).Union(g.Select(s => s.Name)).ToArray()
}
You can make use of ToLookup:
DateTime dt1 = new DateTime(2013, 2, 1);
DateTime dt2 = new DateTime(2013, 3, 1);
DateTime dt3 = new DateTime(2013, 4, 1);
var list1 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt1, Name = "n3",},
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt3, Name = "n3",},
};
var intersected = list1.Select (l => l.DateTime).
Intersect(list2.Select (l => l.DateTime));
var lookup = list1.Union(list2).ToLookup (
slot => slot.DateTime, slot => slot);
lookup.Where (l => intersected.Contains(l.Key)).Select (
slot => new
{
DateTime=slot.Key,
Names=slot.Select (s => s.Name)
});
Which in this case gives the result:
DateTime Names
01/02/2013 00:00 n1
n3
n1
01/03/2013 00:00 n2
n2
You could of course use Names=slot.Select(s => s.Name).Distinct() to get a distinct list of names.

Dictionary of dates and values, finding value of max date per year in LINQ

Have a list like this:
01/01/2009, 120
04/01/2009, 121
30/12/2009, 520
01/01/2010, 100
04/01/2010, 101
31/12/2010, 540
I need to find the last value for each year, e.g. the result would be 520, 540?
var lastValues = records.OrderByDescending(r => r.Date)
.GroupBy(r => r.Date.Year)
.Select(g => g.First().Value);
class Program
{
static void Main()
{
var list = new[]
{
new { Date = new DateTime(2009, 1, 1), Value = 120 },
new { Date = new DateTime(2009, 4, 1), Value = 121 },
new { Date = new DateTime(2009, 12, 30), Value = 520 },
new { Date = new DateTime(2010, 1, 1), Value = 100 },
new { Date = new DateTime(2009, 4, 1), Value = 101 },
new { Date = new DateTime(2010, 12, 31), Value = 540 },
};
var result = list
.GroupBy(x => x.Date.Year)
.Select(g => new { Date = g.Key, MaxValue = g.Max(x => x.Value) });
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}

Traverse list to add elements using .NET

I have a list of objects. Each object has an integer quantity and a DateTime variable which contains a month and year value. I'd like to traverse the list and pad the list by adding missing months (with quantity 0) so that all consecutive months are represented in the list. What would be the best way to accomplish this?
Example:
Original 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 }
One possible algorithm is to keep track of the previous and current months. If the difference between previous and current is 1 month, append current to the result. If the difference is more than one month, add the missing months first, then afterwards 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 make this 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 haven't handled the case where the original list is empty but you should do this in production code.
Lets assume the structure is held as a 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();
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 ...;
}
});
If I am understand correctly with "DateTime" month:
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);
One way is to implement an IEqualityComparer<> of your object, then you can create a list of "filler" objects to add to your existing list using the "Except" extension method. Sort of like 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()));
Considering years, speed and extensibility it can be done as enumerable extension (possibly even using generic property selector).
If dates are already truncated to month and list is ordered before FillMissing is executed, please 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 example form:
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 textbox 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

Categories