LINQ Utilizzo di Max () per selezionare una singola riga


95

Sto usando LINQ su un IQueryable restituito da NHibernate e devo selezionare la riga con il valore massimo (i) in un paio di campi.

Ho semplificato la parte su cui sto attaccando. Devo selezionare l'unica riga dalla mia tabella con il valore massimo in un campo.

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

Questo non è corretto ma non riesco a trovare il modulo giusto.

A proposito, quello che sto effettivamente cercando di ottenere è approssimativamente questo:

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

Ho iniziato con il lambda sopra ma ho usato LINQPad per provare a elaborare la sintassi per la selezione di Max ().

AGGIORNARE

La rimozione di GroupBy è stata la chiave.

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 c'è stata una buona risposta abbastanza lontano da tale domanda: stackoverflow.com/a/6330485/444244
Boggin

Ce ne sono di molto migliori di così ...
M. Babcock

Dai un'occhiata alla risposta .
Sergey Brunov

@Serge Sono d'accordo che morelinq sarebbe il migliore, ma temo che questo progetto abbia degli ostacoli all'aggiunta di nuove librerie.
Boggin

Risposte:


230

Non vedo perché ti stai raggruppando qui.

Prova questo:

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

Un approccio alternativo che itererebbe tablesolo una volta sarebbe questo:

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

Questo è utile se tableè un file IEnumerable<T>che non è presente in memoria o che viene calcolato al volo.


1
Ho eliminato il gruppo e ho scoperto che potevo farlo funzionare: from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Boggin

1
Puoi anche annidare la sintassi lambda: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch

13
@LandonPoch: Non è una buona idea, poiché calcola il numero massimo di N volte dove N è il numero di elementi in table.
Daniel Hilgarth

2
@Daniel Hilgarth: buona cattura! Questo calcola infatti il ​​massimo per ogni riga della tabella. Colpa mia.
Landon Poch

17

Puoi anche fare:

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

13

Puoi raggruppare per stato e selezionare una riga dal gruppo più grande:

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

Il primo First()ottiene il primo gruppo (l'insieme di righe con lo stato più grande); il secondo First()ottiene la prima riga in quel gruppo.
Se lo stato è sempre unqiue, puoi sostituire il secondo First()con Single().


7

Affrontando la prima domanda, se devi prendere diverse righe raggruppate in base a determinati criteri con l'altra colonna con il valore massimo, puoi fare qualcosa del genere:

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

Più un esempio:

Seguire:

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

È uguale a:

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

-1

Semplicemente in una riga:

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

Notare che ci sono due azioni. l'azione interna è per trovare il valore massimo, l'azione esterna è per ottenere l'oggetto desiderato.


Questo metodo è stato discusso nei commenti alla risposta accettata dove è stato sottolineato che era una cattiva idea.
Boggin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.