121 votes

LINQ Utiliser Max() pour sélectionner une seule ligne

J'utilise LINQ sur un IQueryable retourné par NHibernate et j'ai besoin de sélectionner la ligne avec la (les) valeur(s) maximale(s) dans quelques champs.

J'ai simplifié la partie sur laquelle je m'accroche. Je dois sélectionner la ligne de mon tableau qui contient la valeur maximale dans un champ.

var table = new Table { new Row(id: 1, status: 10), new Row(id: 2, status: 20) }

from u in table
group u by 1 into g
where u.Status == g.Max(u => u.Status)
select u

C'est incorrect, mais je n'arrive pas à trouver la bonne forme.

BTW, ce que j'essaie de réaliser est à peu près ceci :

var clientAddress = this.repository.GetAll()
    .GroupBy(a => a)
    .SelectMany(
            g =>
            g.Where(
                a =>
                a.Reference == clientReference && 
                a.Status == ClientStatus.Live && 
                a.AddressReference == g.Max(x => x.AddressReference) && 
                a.StartDate == g.Max(x => x.StartDate)))
    .SingleOrDefault();

J'ai commencé avec la lambda ci-dessus mais j'ai utilisé LINQPad pour essayer de trouver la syntaxe pour sélectionner Max().

MISE À JOUR

La suppression du GroupBy était essentielle.

var all = this.repository.GetAll();

var address = all
            .Where(
                a =>
                a.Reference == clientReference && 
                a.Status == ClientStatus.Live && 
                a.StartDate == all.Max(x => x.StartDate) &&
                a.AddressReference == all.Max(x => x.AddressReference))
            .SingleOrDefault();

274voto

Daniel Hilgarth Points 90722

Je ne vois pas pourquoi vous faites des regroupements.

Essayez ceci :

var maxValue = table.Max(x => x.Status)
var result = table.First(x => x.Status == maxValue);

Une autre approche, qui consisterait à itérer table une seule fois serait celle-ci :

var result = table.OrderByDescending(x => x.Status).First();

Ceci est utile si table est un IEnumerable<T> qui n'est pas présente en mémoire ou qui est calculée à la volée.

21voto

KAPIL SHARMA Points 11

Vous pouvez aussi le faire :

(from u in table
orderby u.Status descending
select u).Take(1);

13voto

SLaks Points 391154

Vous pouvez regrouper par statut et sélectionner une ligne dans le groupe le plus important :

table.GroupBy(r => r.Status).OrderByDescending(g => g.Key).First().First();

Le premier First() obtient le premier groupe (l'ensemble des lignes ayant l'état le plus élevé) ; le second First() obtient la première ligne de ce groupe.
Si l'état est toujours inégal, vous pouvez remplacer la deuxième ligne de commande par une ligne de commande. First() avec Single() .

7voto

Dmitry Komin Points 11

Pour répondre à la première question, si vous avez besoin de prendre plusieurs lignes groupées par certains critères avec l'autre colonne avec la valeur maximale, vous pouvez faire quelque chose comme ceci :

var query =
    from u1 in table
    join u2 in (
        from u in table
        group u by u.GroupId into g
        select new { GroupId = g.Key, MaxStatus = g.Max(x => x.Status) }
    ) on new { u1.GroupId, u1.Status } equals new { u2.GroupId, Status = u2.MaxStatus}
    select u1;

2voto

PavelP Points 11

Qu'en est-il de l'utilisation de Aggregate ?

C'est mieux que

  1. Sélectionner le maximum
  2. Sélection par valeur maximale

puisqu'il ne parcourt le tableau qu'une seule fois.

var maxRow = table.Aggregate(
  (a, b) => a.Status > b.Status ? a : b  // whatever you need to compare
);

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