LINQ Menggunakan Max () untuk memilih satu baris

95

Saya menggunakan LINQ pada IQuerable yang dikembalikan dari NHibernate dan saya perlu memilih baris dengan nilai maksimum di beberapa bidang.

Saya telah menyederhanakan sedikit yang saya pertahankan. Saya perlu memilih satu baris dari tabel saya dengan nilai maksimum dalam satu bidang.

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

Ini tidak benar, tetapi saya tidak dapat menentukan bentuk yang benar.

BTW, apa yang sebenarnya saya coba capai adalah kira-kira ini:

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

Saya mulai dengan lambda di atas tetapi saya telah menggunakan LINQPad untuk mencoba dan mengerjakan sintaks untuk memilih Max ().

MEMPERBARUI

Menghapus GroupBy adalah kuncinya.

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
sumber
kemungkinan duplikat: stackoverflow.com/questions/1101841/…
M.Babcock
@ M.Babcock ada jawaban yang bagus cukup jauh dalam pertanyaan itu: stackoverflow.com/a/6330485/444244
Boggin
Ada yang jauh lebih baik dari itu ...
M.Babcock
Coba lihat jawabannya .
Sergey Brunov
@Serge Saya setuju bahwa morelinq akan menjadi yang terbaik, tetapi saya khawatir proyek ini memiliki hambatan untuk menambahkan perpustakaan baru.
Boggin

Jawaban:

232

Saya tidak mengerti mengapa Anda berkelompok di sini.

Coba ini:

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

Pendekatan alternatif yang tablehanya akan berulang sekali adalah ini:

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

Hal ini berguna jika tableadalah IEnumerable<T>yang tidak hadir dalam memori atau yang dihitung dengan cepat.

Daniel Hilgarth
sumber
1
Saya mengeluarkan pengelompokan tersebut dan ternyata saya bisa membuatnya berfungsi: from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Boggin
1
Anda juga dapat menyusun sintaks lambda: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch
13
@LandonPoch: Itu bukan ide yang bagus, karena ini akan menghitung maksimum N kali dengan N menjadi jumlah item dalam table.
Daniel Hilgarth
2
@Daniel Hilgarth: Tangkapan bagus! Itu sebenarnya akan menghitung maks per setiap baris dalam tabel. Salahku.
Landon Poch
17

Anda juga bisa melakukan:

(from u in table
orderby u.Status descending
select u).Take(1);
KAPIL SHARMA
sumber
13

Anda dapat mengelompokkan berdasarkan status dan memilih baris dari grup terbesar:

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

Yang pertama First()mendapatkan grup pertama (kumpulan baris dengan status terbesar); yang kedua First()mendapat baris pertama dalam grup itu.
Jika status selalu unqiue, Anda bisa mengganti second First()dengan Single().

SLaks
sumber
7

Mengatasi pertanyaan pertama, jika Anda perlu mengambil beberapa baris yang dikelompokkan berdasarkan kriteria tertentu dengan kolom lain dengan nilai maksimal Anda dapat melakukan sesuatu seperti ini:

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
sumber
0

Lebih dari satu contoh:

Mengikuti:

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

Sama dengan:

 select t.*   from nametable t  where t.OrdSeq =
        (select max(t2.OrdSeq) from nametable t2 where t2.FieldPk= t.FieldPk)
SantanaFire
sumber
-1

Cukup dalam satu baris:

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

Perhatikan bahwa ada dua tindakan. tindakan bagian dalam untuk mencari nilai maksimal, tindakan bagian luar untuk mendapatkan objek yang diinginkan.

Shneor
sumber
Metode ini dibahas dalam komentar untuk jawaban yang diterima di mana itu menunjukkan bahwa itu adalah ide yang buruk.
Boggin