Reputation: 107
I have a List<MyClass>
with Status
and Date
fields. I want to return a single MyClass
where the Status = X
.
The problem is there could be more than one in the list, in which case I want to sort on Date
and return the latest one.
Can this be done in LINQ in a single expression?
Upvotes: 2
Views: 1447
Reputation: 30492
So you a sequence of MyClass
objects and an object X
, and you want to find the newest MyClass
object that has a value for Status
equal to X
var result = myList.Where(myItem => myItem.Status == X)
.OrderByDescending(myItem => myItem.Date)
.FirstOrDefault();
Although this will work, the sorting is not very efficient: after you've find the first element, it sorts the 2nd, 3rd, etc, while you know it will not use these other elements, so why sort them? If you use Aggregate
you will only have to enumerate once
var result = myList.Where(...)
.Aggregate( (newestItem, nextItem) => (newestItem.Date < nextItem.Date) ?
newestItem : nextItem);
This puts the first element in newestItem and scans the rest of the list. If any nextItem has a new data, then put this next item in newestItem, otherwise do not change newestItem. At the end return newestItem, which is the one with the newest date that you want.
This works only if you are certain that there is at least one remaining item after the where. If it must also work with empty lists, consider creating an extension function:
static TResult GetNewestOrDefault<TSource, TResult>(this IEnumerable<TSource> source,
Func<Tsource, DateTime> dateSelector)
{
var enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{ // at least one element; initialize newest:
TSource newestElement = enumerator.Current;
DateTime newestDate = dateSelector(newest);
// scan the rest of the sequence and check if newer:
while (enumerator.MoveNext())
{
Tsource nextElement = enumerator.Current;
Datetime nextDate = dateSelector(nextElement);
// if next element has a newer date, remember it as newest:
if (newestDate < nextDate
{
newestElement = nextElement;
newestDate = nextDate,
}
}
// scanned all elements exactly once
return newestElement;
}
else
// empty sequence, return default
return default(TResult);
}
Usage:
MyClass result = myList.Where(myItem => myItem.Status == X)
.GetNewestOrDefault();
This will scan your sequence exactly once.
Upvotes: 0
Reputation: 13498
If list
already located at memory try this:
var answer = list.Where(x => x.Status == X).OrderBy(x => x.Date).LastOrDefault();
At case of Entity Framework try another approach:
var answer = context.Table.Where(x => x.Status == X).OrderByDescending(x => x.Date).FirstOrDefault();
Upvotes: 1
Reputation: 2262
You can use lambda expression:
var yourResult = dbo.YourList.OrderByDescending(t=>t.Date).FirstOrDefault(t=>t.Status == 'X');
Upvotes: 3