Mittwoch, 24. Juni 2009

Neuesten Datensatz ermitteln mit LinqToObjects

Um aus einer Auflistung von Objekten mit einem Timestamp das jeweils aktuellsten zu bekommen müssen mehrere Linq-Operatoren verknüpft werden.

Ausgangslage soll folgende Liste von Objekten des Typs WorkItem sein:

List<WorkItem> workitems = new List<WorkItem>();
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-17", TimeStamp = new DateTime(2009, 06, 17) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-18", TimeStamp = new DateTime(2009, 06, 18) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-19", TimeStamp = new DateTime(2009, 06, 19) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-20", TimeStamp = new DateTime(2009, 06, 20) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-18", TimeStamp = new DateTime(2009, 06, 18) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-19", TimeStamp = new DateTime(2009, 06, 19) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-21", TimeStamp = new DateTime(2009, 06, 21) });

Ziel ist nun, pro UserId das WorkItem mit dem höchsten TimeStamp zu ermitteln. Zunächst muss der höchste TimeStamp pro Benutzer ermittelt werden. Das geht noch recht einfach mit einem grouped max:

from wi in workitems
group wi by wi.UserId into g
select new { UserId = g.Key, TimeStamp = g.Max(w => w.TimeStamp) }

Das Ergebnis enthält nun sozusagen die Schlüsselspalten, um eine Filterung durchzuführen. Diese muss nun mittels Join mit der originalen Liste durchgeführt werden. Problem hierbei ist, dass im equals des join-Operators nur ein Kriterium angegeben werden kann. Der Schlüssel für die Filterung besteht aber aus 2 Kriterien: UserId und TimeStamp. Die Lösung ist, in diesem Fall den Join über einen anonymen Typ zu realisieren:

var results = from workitem in workitems
join newestitem in
(from wi in workitems
group wi by wi.UserId into g
select new { UserId = g.Key, TimeStamp = g.Max(w => w.TimeStamp) })
on new { workitem.UserId, workitem.TimeStamp } equals new { newestitem.UserId, newestitem.TimeStamp }
select workitem;

Kick It on dotnet-kicks.de