¿Cómo puedo obtener LINQ para devolver el objeto que tiene el valor máximo para una propiedad determinada?

Si tengo una clase que se parece a:

public class Item { public int ClientID { get; set; } public int ID { get; set; } } 

Y una colección de esos artículos …

 List items = getItems(); 

¿Cómo puedo usar LINQ para devolver el único objeto “Artículo” que tiene la ID más alta?

Si hago algo como:

 items.Select(i => i.ID).Max(); 

Solo obtendré la ID más alta, cuando lo que realmente quiero devolver es el objeto Item en sí, ¿cuál tiene la ID más alta? Quiero que devuelva un solo objeto “Artículo”, no un int.

Esto recorrerá solo una vez.

 Item biggest = items.Aggregate((i1,i2) => i1.ID > i2.ID ? i1 : i2); 

Gracias Nick – Aquí está la prueba

 class Program { static void Main(string[] args) { IEnumerable items1 = new List() { new Item(){ ClientID = 1, ID = 1}, new Item(){ ClientID = 2, ID = 2}, new Item(){ ClientID = 3, ID = 3}, new Item(){ ClientID = 4, ID = 4}, }; Item biggest1 = items1.Aggregate((i1, i2) => i1.ID > i2.ID ? i1 : i2); Console.WriteLine(biggest1.ID); Console.ReadKey(); } } public class Item { public int ClientID { get; set; } public int ID { get; set; } } 

Reorganiza la lista y obtén el mismo resultado

 .OrderByDescending(i=>i.id).Take(1) 

Con respecto a la preocupación por el rendimiento, es muy probable que este método sea teóricamente más lento que un enfoque lineal. Sin embargo, en realidad, la mayoría de las veces no estamos tratando con el conjunto de datos lo suficientemente grande como para hacer una diferencia.

Si el rendimiento es una preocupación principal, la respuesta de Seattle Leonard debería darle complejidad de tiempo lineal. Alternativamente, también puede considerar comenzar con una estructura de datos diferente que devuelve el elemento de valor máximo a tiempo constante.

Use MaxBy del proyecto morelinq :

 items.MaxBy(i => i.ID); 
 int max = items.Max(i => i.ID); var item = items.First(x => x.ID == max); 

Esto supone que hay elementos en la colección de elementos, por supuesto.

En caso de que no desee utilizar MoreLINQ y desee obtener tiempo lineal, también puede usar Aggregate :

 var maxItem = items.Aggregate( new { Max = Int32.MinValue, Item = (Item)null }, (state, el) => (el.ID > state.Max) ? new { Max = el.ID, Item = el } : state).Item; 

Esto recuerda el elemento máximo actual ( Item ) y el valor máximo actual ( Item ) en un tipo anónimo. Luego solo escoge la propiedad Item . Esto de hecho es un poco feo y podrías envolverlo en el método de extensión MaxBy para obtener lo mismo que con MoreLINQ:

 public static T MaxBy(this IEnumerable items, Func f) { return items.Aggregate( new { Max = Int32.MinValue, Item = default(T) }, (state, el) => { var current = f(el.ID); if (current > state.Max) return new { Max = current, Item = el }; else return state; }).Item; } 

O puede escribir su propio método de extensión:

 static partial class Extensions { public static T WhereMax(this IEnumerable items, Func selector) { if (!items.Any()) { throw new InvalidOperationException("Empty input sequence"); } var comparer = Comparer.Default; T maxItem = items.First(); U maxValue = selector(maxItem); foreach (T item in items.Skip(1)) { // Get the value of the item and compare it to the current max. U value = selector(item); if (comparer.Compare(value, maxValue) > 0) { maxValue = value; maxItem = item; } } return maxItem; } } 

prueba esto:

 var maxid = from i in items group i by i.clientid int g select new { id = g.Max(i=>i.ID } 

Podría usar una variable capturada.

 Item result = items.FirstOrDefault(); items.ForEach(x => { if(result.ID < x.ID) result = x; }); 

En LINQ puedes resolverlo de la siguiente manera:

 Item itemMax = (from i in items let maxId = items.Max(m => m.ID) where i.ID == maxId select i).FirstOrDefault(); 

Este es un método de extensión derivado de la respuesta de @Seattle Leonard:

  public static T GetMax(this IEnumerable data, Func f) where U:IComparable { return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2); }