Si j'ai un cours qui ressemble à:
public class Item
{
public int ClientID { get; set; }
public int ID { get; set; }
}
Et une collection de ces objets ...
List<Item> items = getItems();
Comment utiliser LINQ pour renvoyer l'unique objet "Item" qui a le plus grand ID?
Si je fais quelque chose comme:
items.Select(i => i.ID).Max();
Je n'obtiendrai que l'ID le plus élevé, lorsque ce que je veux réellement retourner est l'objet Item lui-même qui a l'ID le plus élevé. Je veux qu'il retourne un seul objet "Item", pas un int.
Cela ne fera qu'une boucle.
Item biggest = items.Aggregate((i1,i2) => i1.ID > i2.ID ? i1 : i2);
Merci Nick - Voici la preuve
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; }
}
Réorganiser la liste et obtenir le même résultat
.OrderByDescending(i=>i.id).Take(1)
En ce qui concerne la performance, il est très probable que cette méthode est théoriquement plus lente qu’une approche linéaire. Cependant, en réalité, la plupart du temps, nous ne traitons pas avec un ensemble de données suffisamment volumineux pour faire une différence.
Si la performance est une préoccupation majeure, la réponse de Seattle Leonard devrait vous donner une complexité temporelle linéaire. Sinon, vous pouvez également envisager de commencer avec une structure de données différente qui renvoie l'élément de valeur maximale à temps constant.
int max = items.Max(i => i.ID);
var item = items.First(x => x.ID == max);
Cela suppose qu'il y a bien sûr des éléments dans la collection d'objets.
Si vous ne voulez pas utiliser MoreLINQ et obtenir un temps linéaire, vous pouvez également utiliser 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;
Cela rappelle l’élément maximal actuel (Item
) et la valeur maximale actuelle (Item
) dans un type anonyme. Ensuite, il vous suffit de choisir la propriété Item
. C'est en effet un peu moche et vous pourriez l'envelopper dans la méthode d'extension MaxBy
pour obtenir la même chose qu'avec 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;
}
Ou vous pouvez écrire votre propre méthode d'extension:
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;
}
}
essaye ça:
var maxid = from i in items
group i by i.clientid int g
select new { id = g.Max(i=>i.ID }
Ceci est une méthode d'extension dérivée de la réponse 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);
}
Dans LINQ, vous pouvez le résoudre de la manière suivante:
Item itemMax = (from i in items
let maxId = items.Max(m => m.ID)
where i.ID == maxId
select i).FirstOrDefault();
Vous pouvez utiliser une variable capturée.
Item result = items.FirstOrDefault();
items.ForEach(x =>
{
if(result.ID < x.ID)
result = x;
});