LINQ Utilisation de Max () pour sélectionner une seule ligne


95

J'utilise LINQ sur un IQueryable renvoyé par NHibernate et je dois 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 ma table avec 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

Ceci est incorrect mais je ne parviens pas à trouver le bon formulaire.

BTW, ce que j'essaie réellement 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 le lambda ci-dessus mais j'ai utilisé LINQPad pour essayer de trouver la syntaxe pour sélectionner le Max ().

METTRE À JOUR

La suppression de GroupBy était la clé.

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


@ M.Babcock, il y avait une bonne réponse assez loin dans cette question: stackoverflow.com/a/6330485/444244
Boggin

Il y en a beaucoup mieux que ça ...
M.Babcock

Jetez un œil à la réponse .
Sergey Brunov

@Serge Je suis d'accord que morelinq serait le meilleur mais je crains que ce projet n'ait des obstacles à l'ajout de nouvelles bibliothèques.
Boggin

Réponses:


230

Je ne vois pas pourquoi vous groupez ici.

Essaye ça:

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

Une autre approche qui n'itérerait tablequ'une seule fois serait la suivante:

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

Ceci est utile s'il tables'agit d'un élément IEnumerable<T>qui n'est pas présent en mémoire ou qui est calculé à la volée.


1
J'ai retiré le groupement et j'ai trouvé que je pouvais le faire fonctionner: from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Boggin

1
Vous pouvez également imbriquer la syntaxe lambda: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch

13
@LandonPoch: Ce n'est pas une bonne idée, car cela calculerait le maximum N fois avec N étant le nombre d'éléments dans table.
Daniel Hilgarth

2
@Daniel Hilgarth: Bonne prise! Cela permettrait en fait de calculer le maximum pour chaque ligne du tableau. Ma faute.
Landon Poch

17

Vous pouvez également faire:

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

13

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

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

Le premier First()obtient le premier groupe (l'ensemble de lignes avec le plus grand statut); le second First()obtient la première ligne de ce groupe.
Si le statut est toujours unqiue, vous pouvez remplacer le second First()par Single().


7

Pour répondre à la première question, si vous devez prendre plusieurs lignes regroupées selon certains critères avec l'autre colonne avec une 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;

0

Plus un exemple:

Suivre:

 qryAux = (from q in qryAux where
            q.OrdSeq == (from pp in Sessao.Query<NameTable>() where pp.FieldPk
            == q.FieldPk select pp.OrdSeq).Max() select q);

Équivaut à:

 select t.*   from nametable t  where t.OrdSeq =
        (select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)

-1

Simplement en une seule ligne:

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

Notez qu'il y a deux actions. l'action intérieure est pour trouver la valeur maximale, l'action extérieure est pour obtenir l'objet désiré.


Cette méthode a été discutée dans les commentaires à la réponse acceptée où il a été souligné que c'était une mauvaise idée.
Boggin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.