177 votes

Comment puis-je faire en sorte que LINQ renvoie l'objet qui a la valeur maximale pour une propriété donnée ?

Si j'ai une classe qui ressemble à :

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

Et une collection de ces articles...

List<Item> items = getItems();

Comment puis-je utiliser LINQ pour retourner l'unique objet "Item" qui a l'ID le plus élevé ?

Si je fais quelque chose comme :

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

Je n'obtiendrai que l'ID le plus élevé, alors que je veux en fait que l'objet Item lui-même soit retourné avec l'ID le plus élevé ? Je veux qu'il renvoie un objet "Item" unique, pas un nombre entier.

0 votes

En plus des réponses sur cette page, j'ai pensé qu'il valait la peine de mentionner cette réponse aussi : stackoverflow.com/a/1101979/4880924 Quelqu'un peut-il répondre mieux que Jon Skeet ? Il fournit un raisonnement pour sa réponse.

185voto

Seattle Leonard Points 2395

La boucle ne sera parcourue qu'une seule fois.

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éorganisez la liste et obtenez le même résultat.

1 votes

J'ai voté pour parce que j'aime le concept. Je n'ai aucune idée si ce code fera réellement ce qui a été demandé cependant.

0 votes

@Nick : Oui, cela fonctionnera. Voir Agrégat y réduire le site

1 votes

J'ai eu ce même problème plus d'une fois, et c'est la solution la plus élégante que j'ai trouvée. Merci !

89voto

Codism Points 1324
.OrderByDescending(i=>i.id).First(1)

En ce qui concerne le problème de performance, il est très probable que cette méthode soit théoriquement plus lente qu'une approche linéaire. Cependant, dans la réalité, la plupart du temps, nous n'avons pas affaire à un ensemble de données suffisamment important pour faire une différence.

Si la performance est une préoccupation majeure, la réponse de Seattle Leonard devrait vous donner une complexité en temps linéaire. Vous pouvez également envisager de commencer par une structure de données différente qui renvoie l'élément de valeur maximale en temps constant.

13 votes

Ça marche, mais c'est nlogn au lieu de temps linéaire.

4 votes

Tzaman : en théorie Le système LINQ pourrait identifier le modèle "orderby().take()" et utiliser un algorithme en temps linéaire, mais vous avez raison de dire qu'il ne le fait probablement pas.

45 votes

.OrderByDescending(i=>i.id).First() retournerait l'objet lui-même, plutôt qu'une énumération avec 1 élément.

40voto

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

Cela suppose qu'il y ait des éléments dans la collection d'éléments, bien sûr.

1 votes

"Where" renvoie tous les éléments avec la valeur maximale, peut-être que nous voulons seulement le premier, dans ce cas "First" serait le meilleur.

1 votes

+1 pour la clarté ; je suis dans le camp que tzaman décrit ci-dessus (et je vais peut-être essayer morelinq aussi...)

0 votes

Ce code renvoie l'article avec l'ID maximum. Comme l'a dit Jon Skeet, Max renvoie la valeur maximale, et non l'élément contenant la valeur maximale.

32voto

tzaman Points 13190

Utilisez MaxBy de la morelinq projet :

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

8 votes

@Reed : Je suppose que vous avez compris pourquoi maintenant... mais pour les autres lecteurs : Max renvoie la valeur maximale, pas l'élément contenant la valeur maximale. Notez que MaxBy se trouve également dans System.Interactive dans le cadre de Reactive Extensions.

1 votes

Oui, je l'oublie toujours. Je continue à l'utiliser en pensant qu'il est correct, aussi.

1 votes

Il n'est pas nécessaire d'écrire votre propre méthode (ou d'ajouter une autre dépendance) ; Seattle Leonard et NickLarson proposent tous deux des méthodes simples en une ligne qui font la même chose.

6voto

Tomas Petricek Points 118959

Dans le cas où vous ne voulez pas utiliser MoreLINQ et que vous voulez 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 permet de se souvenir de l'élément maximal actuel ( Item ) et la valeur maximale actuelle ( Item ) dans un type anonyme. Ensuite, il suffit de choisir le Item propriété. C'est en effet un peu laid et vous pourriez l'intégrer dans la propriété 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X