web-dev-qa-db-fra.com

Comment puis-je obtenir que LINQ retourne l'objet qui a la valeur max pour une propriété donnée?

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.

126
FrankTheTank

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

137
Seattle Leonard
.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.

51
Codism
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.

31
Nick Larsen

Utilisez MaxBy à partir du projet morelinq :

items.MaxBy(i => i.ID);
29
tzaman

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;
}
5
Tomas Petricek

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;
    }
}
4
ollb

essaye ça:

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

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);
 }
2
Paul Richards

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();
1
Ohlin

Vous pouvez utiliser une variable capturée.

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