c# - nodo - ¿Cómo puedo obtener LINQ para devolver el objeto que tiene el valor máximo para una propiedad determinada?
manejo de xml en c# (10)
Esta pregunta ya tiene una respuesta aquí:
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<Item> 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.
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();
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<T> items, Func<T, int> 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;
}
Este es un método de extensión derivado de la respuesta de @Seattle Leonard:
public static T GetMax<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
{
return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
}
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<Item> items1 = new List<Item>()
{
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
O puede escribir su propio método de extensión:
static partial class Extensions
{
public static T WhereMax<T, U>(this IEnumerable<T> items, Func<T, U> selector)
{
if (!items.Any())
{
throw new InvalidOperationException("Empty input sequence");
}
var comparer = Comparer<U>.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;
}
}
Podría usar una variable capturada.
Item result = items.FirstOrDefault();
items.ForEach(x =>
{
if(result.ID < x.ID)
result = x;
});
prueba esto:
var maxid = from i in items
group i by i.clientid int g
select new { id = g.Max(i=>i.ID }
.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.
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.