List with dates, addHours to list linq - c#

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

Related

Reversing a list of objects with a timespan property, while maintaining the time difference between the objects

I'd like to reverse a list of objects with a TimeSpan property, which should maintain it's TimeSpan difference when reversing.
To give an example, consider a route from A to D with the following TimeSpans:
(A 12:00), (B 12:15), (C 12:40), (D 13:40).
Between A and B there is a 15 minute difference, between B and C there is a 25 minute difference and so on. I'd like to reverse this list in an efficient manner, where the result list would look like:
(D: 12:00), (C 13:00), (B 13:25), (A 13:40).
My first idea was creating a list of time differences and using that and the start time to create the new objects with the correct times, however I feel like the solution could be better.
Edit: Added my (working) sample solution. Any feedback is appreciated.
private IList<Activity> ReverseActivities(IList<Activity> activities)
{
IList<TimeSpan> timeDifferences = GetTimeDifferences(activities);
IList<Activity> resultList = new List<Activity>();
TimeSpan timeOfDay = activities.First().TimeOfDay;
for (int i = activities.Count - 1; i >= 0; i--)
{
resultList.Add(new Activity(activities[i].Name, timeOfDay));
timeOfDay = timeOfDay.Add(timeDifferences[i]);
}
return resultList;
}
private IList<TimeSpan> GetTimeDifferences(IList<Activity> activities)
{
IList<TimeSpan> timeDifferences = new List<TimeSpan>();
Activity prev = activities.First();
if (activities.Count > 1)
{
foreach (var curr in activities)
{
timeDifferences.Add(curr.TimeOfDay - prev.TimeOfDay);
prev = curr;
}
}
return timeDifferences;
}
Activity looks as follows:
public class Activity
{
public Activity(string name, TimeSpan timeOfDay)
{
this.Name = name;
this.TimeOfDay = timeOfDay;
}
public string Name { get; }
public TimeSpan TimeOfDay { get; }
}
One trick we can use is to have a single loop that finds the corresponding item from the end of the list based on the current index. We can do this like:
for (int i = 0; i < activities.Count; i++)
var correspondingIndex = activities.Count - i - 1;
Notice that:
When i is 0, correspondingIndex is the last index in the array.
When i is 1, correspondingIndex is the second-to-last index in the array.
When i is activities.Count - 1 (the last index), correspondingIndex is 0
Using this trick, we can get the corresponding time differences at the same time as we populate a new list of Activity objects.
Hopefully this code makes it a little clearer:
public static IList<Activity> ReverseActivities(IList<Activity> activities)
{
// If activities is null or contains less than 2 items, return it
if ((activities?.Count ?? 0) < 2) return activities;
// This will contain the reversed list
var reversed = new List<Activity>();
for (int i = 0; i < activities.Count; i++)
{
// Get the corresponding index from the end of the list
var correspondingIndex = activities.Count - i - 1;
// Get the timespan from the corresponding items from the end of the list
var timeSpan = i == 0
? TimeSpan.Zero
: activities[correspondingIndex + 1].TimeOfDay -
activities[correspondingIndex].TimeOfDay;
// The new TimeOfDay will be the previous item's TimeOfDay plus the TimeSpan above
var timeOfDay = i == 0
? activities[i].TimeOfDay
: reversed[i - 1].TimeOfDay + timeSpan;
reversed.Add(new Activity(activities[correspondingIndex].Name, timeOfDay));
}
return reversed;
}
In use, this would look like:
var original = new List<Activity>
{
new Activity("A", new TimeSpan(0, 12, 0)),
new Activity("B", new TimeSpan(0, 12, 15)),
new Activity("C", new TimeSpan(0, 12, 40)),
new Activity("D", new TimeSpan(0, 13, 40))
};
var reversed = ReverseActivities(original);
Here's the output in the debug window (compare original and reversed):
This is quite simple using a bit of TimeSpan maths.
IList<Activity> input = new List<Activity>()
{
new Activity("A", TimeSpan.Parse("12:00")),
new Activity("B", TimeSpan.Parse("12:15")),
new Activity("C", TimeSpan.Parse("12:40")),
new Activity("D", TimeSpan.Parse("13:40")),
};
TimeSpan min = input.Min(x => x.TimeOfDay);
TimeSpan max = input.Max(x => x.TimeOfDay);
IList<Activity> output =
input
.Select(x => new Activity(
x.Name,
x.TimeOfDay.Subtract(max).Duration().Add(min)))
.OrderBy(x => x.TimeOfDay)
.ToList();
That gives me:
I tested this and it works:
DateTime[] times = { new DateTime(2020, 06, 17, 12, 00, 00),
new DateTime(2020, 06, 17, 12, 15, 00), new DateTime(2020, 06, 17, 12, 40, 00),
new DateTime(2020, 06, 17, 13, 40, 00) };
List<DateTime> newTimes = new List<DateTime>();
newTimes.Add(times[0]);
for (int i = 1; i < times.Length; i++) {
DateTime d = newTimes[i - 1].Add(times[times.Length - i] - times[times.Length - i - 1]);
newTimes.Add(d);
}
Using LinkedList:
static void Main(string[] args)
{
var list = new List<Location>
{
new Location{Name = "A", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 0, 0)) },
new Location{Name = "B", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 15, 0)) },
new Location{Name = "C", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 40, 0)) },
new Location{Name = "D", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(13, 40, 0)) },
};
var route = new LinkedList<Location>(list);
WriteToConsole("Before: ", route);
var reversedRoute = Reverse(route);
Console.WriteLine();
WriteToConsole("After: ", reversedRoute);
Console.WriteLine(); Console.ReadKey();
}
public static LinkedList<Location> Reverse(LinkedList<Location> route)
{
LinkedList<Location> retVal = new LinkedList<Location>();
DateTimeOffset timeOffset = DateTimeOffset.MinValue;
var currentNode = route.Last;
while (currentNode != null)
{
var next = currentNode.Next;
if (next == null)
{
// last node, use the first node offset
timeOffset = DateTimeOffset.MinValue.Add(route.First.Value.TimeOffset - timeOffset);
}
else
{
timeOffset = timeOffset.Add(next.Value.TimeOffset - currentNode.Value.TimeOffset);
}
retVal.AddLast(new Location { Name = currentNode.Value.Name, TimeOffset = timeOffset });
currentNode = currentNode.Previous;
}
return retVal;
}
public static void WriteToConsole(string title, LinkedList<Location> route)
{
Console.Write($"{title}: ");
foreach (var i in route)
{
Console.Write($"\t({i.Name}, {i.TimeOffset.Hour:D2}:{i.TimeOffset.Minute:D2})");
}
}

DateTime processing methodology

I'm facing an issue which I can't find a proper and elegant solution. I have a List of Videos, which is a class that contains informations about a video. Among those informations there is a startDate,endDate and an cameraId property.
My current database has the following values:
startDate endDate
I want to iterate through those values and when a video is within 5 minutes difference from the last one and has the same cameraId it should be counted as one. But I can't find a proper nor elegant way to accomplish this task.
The output for the videos list shown above should be
1st: 2013:03:01 18:25:26 -> 2013-03-01 18:34:29
2nd: 2013:03:01 18:40:26 -> 2013:03:01 18:59:29
This is the code I have so far:
private void ProcessVideos(List<Video> videos)
{
bool isSameVideo = false;
Video lastVideo = null;
//debugar e ver esquema do ultimo valor do database
DateTime startDate = DateTime.MinValue;
DateTime endDate = DateTime.MinValue;
for (int i = 1; i < videos.Count; i++)
{
TimeSpan timeSpan = new TimeSpan(videos[i].DataInicio.Ticks - videos[i - 1].DataFim.Ticks);
if (timeSpan.Minutes > 0 && timeSpan.Minutes < 5 && videos[i].IdCamera == videos[i - 1].IdCamera)
{
if (!isSameVideo)
{
isSameVideo = true;
startDate = videos[i - 1].DataInicio;
endDate = videos[i].DataFim;
}
else
{
endDate = videos[i].DataFim;
}
}
else
{
if (isSameVideo)
{
i++;
isSameVideo = false;
Debug.WriteLine("inicio: {0} fim: {1}", startDate, endDate);
startDate = DateTime.MinValue;
endDate = DateTime.MinValue;
}
Debug.WriteLine("inicio: {0} fim: {1}", videos[i - 1].DataInicio, videos[i - 1].DataFim);
}
}
if (startDate != DateTime.MinValue)
{
Debug.WriteLine("inicio: {0} fim: {1}", startDate, endDate);
}
}
The main question is: What is a good logic to iterate through those values and output a combinations of values according to the timespan specification?
I created a small example to show you:
My container object:
internal class Container
{
public int Id { get; set; }
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
public override string ToString()
{
return "ID " + Id + ": " + Start + " -> " + Stop;
}
}
My method:
private static IEnumerable<Container> DoMerge(List<Container> elements, TimeSpan maxDiff)
{
var closedContainers = new List<Container>();
var lastContainers = new Dictionary<int, Container>();
foreach (Container container in elements.OrderBy(e => e.Start))
{
//First case, no previous container
if (!lastContainers.ContainsKey(container.Id))
{
lastContainers[container.Id] = container;
}
else if (container.Start - lastContainers[container.Id].Stop > maxDiff)
//We have a container, but not in our windows of 5 minutes
{
closedContainers.Add(lastContainers[container.Id]);
lastContainers[container.Id] = container;
}
else
{
//We have to merge our two containers
lastContainers[container.Id].Stop = container.Stop;
}
}
//We have now to put all "lastContainer" in our final list
foreach (KeyValuePair<int, Container> lastContainer in lastContainers)
{
closedContainers.Add(lastContainer.Value);
}
return closedContainers;
}
And we just have to give our max timespan and list of elements:
private static void Main(string[] args)
{
var elements = new List<Container>
{
new Container {Id = 1, Start = new DateTime(2013, 3, 1, 18, 25, 26), Stop = new DateTime(2013, 3, 1, 18, 27, 29)},
new Container {Id = 1, Start = new DateTime(2013, 3, 1, 18, 30, 26), Stop = new DateTime(2013, 3, 1, 18, 34, 29)},
new Container {Id = 1, Start = new DateTime(2013, 3, 1, 18, 40, 26), Stop = new DateTime(2013, 3, 1, 18, 52, 29)},
new Container {Id = 1, Start = new DateTime(2013, 3, 1, 18, 55, 26), Stop = new DateTime(2013, 3, 1, 18, 59, 29)},
};
foreach (Container container in DoMerge(elements, TimeSpan.FromMinutes(5)))
{
Console.WriteLine(container);
}
Console.ReadLine();
}
This give me your expected results we two objects lefts.
Result with the mentionned data:
Here's a solution. The crux of the method is shown in the ExtractVidTimes method. The rest is just for creating the sample data
[TestFixture]
public class TestyMcTest
{
public class Vid
{
public int CamId;
public DateTime Start;
public DateTime End;
}
[Test]
public void Test()
{
var list = new List<Vid>
{
//=====Combination1=======
new Vid
{
CamId = 1,
Start = new DateTime(2000, 1, 1, 0, 0, 0),
End = new DateTime(2000, 1, 1, 0, 3, 0)
},
new Vid
{
CamId = 1,
Start = new DateTime(2000, 1, 1, 0, 5, 0),
End = new DateTime(2000, 1, 1, 0, 7, 0)
},
//=====Combination2=======
new Vid
{
CamId = 1,
Start = new DateTime(2000, 1, 1, 0, 15, 0),
End = new DateTime(2000, 1, 1, 0, 18, 0)
},
//=====Combination3=======
new Vid
{
CamId = 2,
Start = new DateTime(2000, 1, 1, 0, 0, 0),
End = new DateTime(2000, 1, 1, 0, 3, 0)
},
//=====Combination4=======
new Vid
{
CamId = 2,
Start = new DateTime(2000, 1, 1, 0, 10, 0),
End = new DateTime(2000, 1, 1, 0, 13, 0)
}
};
//here is your list of vids grouped by the cam id
var result = ExtractVidTimes(list);
}
//THE METHOD
private static List<List<Vid>> ExtractVidTimes(List<Vid> list)
{
//Group by cam ID
var vidGroups = list.GroupBy(vid => vid.CamId).ToList();
//extract vids with aggregate times
var result = vidGroups.Select(vids =>
{
var vidTimes = new List<Vid>();
var finalVid = vids.OrderBy(vid=> vid.Start).Aggregate((a, b) =>
{
if (a.End.AddMinutes(5) > b.Start)
{
a.End = b.End;
return a;
}
vidTimes.Add(a);
return b;
});
vidTimes.Add(finalVid);
return vidTimes;
}).ToList();
//return result.SelectMany(x=>x); //if you want a List<vid> return ed instead of a nested list
return result;
}
}

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.

How to diff two lists and get net changes

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

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

Categories