LINQ Korzystanie z Max (), aby wybrać pojedynczy wiersz

98

Używam LINQ na IQueryable zwrócony z NHibernate i muszę wybrać wiersz z maksymalną wartością (wartościami) w kilku polach.

Uprościłem kawałek, na którym się trzymam. Muszę wybrać jeden wiersz z mojej tabeli z maksymalną wartością w jednym polu.

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

To jest niepoprawne, ale nie mogę opracować odpowiedniej formy.

A tak przy okazji, to, co faktycznie próbuję osiągnąć, to mniej więcej to:

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

Zacząłem od powyższej lambdy, ale używałem LINQPad do wypróbowania składni wyboru Max ().

AKTUALIZACJA

Usunięcie GroupBy było kluczowe.

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();
Boggin
źródło
możliwy duplikat: stackoverflow.com/questions/1101841/…
M.Babcock
@ M.Babcock w tym pytaniu była dobra odpowiedź: stackoverflow.com/a/6330485/444244
Boggin
Są znacznie lepsze niż to ...
M.Babcock
Spójrz na odpowiedź .
Sergey Brunov
@Serge Zgadzam się, że morelinq byłby najlepszy, ale obawiam się, że ten projekt ma przeszkody w dodawaniu nowych bibliotek.
Boggin

Odpowiedzi:

235

Nie rozumiem, dlaczego się tutaj grupujesz.

Spróbuj tego:

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

Alternatywnym podejściem, które można by powtórzyć tabletylko raz, byłoby:

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

Jest to przydatne, jeśli tablejest to IEnumerable<T>, że nie jest obecny w pamięci lub że jest obliczana na bieżąco.

Daniel Hilgarth
źródło
1
Wyciągnąłem zgrupowanie i stwierdziłem, że mogę to uruchomić: from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Boggin
1
Możesz także zagnieżdżać składnię lambda: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch,
15
@LandonPoch: To nie jest dobry pomysł, ponieważ obliczyłoby to maksymalne N razy, gdzie N to liczba elementów w table.
Daniel Hilgarth,
2
@Daniel Hilgarth: Dobry chwyt! To faktycznie obliczyłoby maksimum na każdy wiersz w tabeli. Mój błąd.
Landon Poch
18

Możesz też:

(from u in table
orderby u.Status descending
select u).Take(1);
KAPIL SHARMA
źródło
13

Możesz grupować według stanu i wybrać wiersz z największej grupy:

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

Pierwsza First()pobiera pierwszą grupę (zestaw wierszy o największym statusie); druga First()otrzymuje pierwszy wiersz w tej grupie.
Jeśli stan jest zawsze unqiue można zastąpić drugą First()z Single().

SLaks
źródło
7

Odpowiadając na pierwsze pytanie, jeśli chcesz wziąć kilka wierszy pogrupowanych według określonych kryteriów z drugą kolumną o maksymalnej wartości, możesz zrobić coś takiego:

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;
Dmitry Komin
źródło
0

Jeszcze jeden przykład:

Podążać:

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

Równa się:

 select t.*   from nametable t  where t.OrdSeq =
        (select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)
SantanaFire
źródło
-1

Po prostu w jednej linii:

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

Zauważ, że są dwie akcje. wewnętrzna akcja służy do znalezienia maksymalnej wartości, zewnętrzna akcja służy do uzyskania pożądanego obiektu.

Shneor
źródło
Ta metoda została omówiona w komentarzach do przyjętej odpowiedzi, gdzie wskazano, że to zły pomysł.
Boggin