LINQ ใช้ Max () เพื่อเลือกแถวเดียว


98

ฉันใช้ LINQ บน IQueryable ที่ส่งคืนจาก NHibernate และฉันต้องการเลือกแถวที่มีค่าสูงสุดในสองฟิลด์

ฉันได้ทำให้บิตที่ฉันยึดติดง่ายขึ้น ฉันต้องการเลือกหนึ่งแถวจากตารางของฉันด้วยค่าสูงสุดในหนึ่งฟิลด์

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

สิ่งนี้ไม่ถูกต้อง แต่ฉันไม่สามารถหาแบบฟอร์มที่ถูกต้องได้

BTW สิ่งที่ฉันพยายามจะบรรลุคือประมาณนี้:

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

ฉันเริ่มต้นด้วยแลมบ์ดาข้างต้น แต่ฉันใช้ LINQPad เพื่อลองใช้ไวยากรณ์สำหรับการเลือก Max ()

อัปเดต

การลบ GroupBy เป็นกุญแจสำคัญ

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

อาจซ้ำกันได้: stackoverflow.com/questions/1101841/…
M.Babcock

@ M.Babcock มีคำตอบที่ดีในคำถามนั้นอยู่ค่อนข้างไกล: stackoverflow.com/a/6330485/444244
Boggin

ยังมีอีกหลายอย่างที่ดีไปกว่านั้น ...
M.Babcock

ลองดูที่คำตอบ
Sergey Brunov

@Sergeฉันยอมรับว่าmorelinqจะดีที่สุด แต่ฉันกลัวว่าโครงการนี้มีอุปสรรคในการเพิ่มไลบรารีใหม่
Boggin

คำตอบ:


238

ฉันไม่เห็นว่าทำไมคุณถึงจัดกลุ่มที่นี่

ลองสิ่งนี้:

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

แนวทางอื่นที่จะวนซ้ำtableเพียงครั้งเดียวจะเป็นดังนี้:

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

สิ่งนี้มีประโยชน์หากtableเป็นสิ่งIEnumerable<T>ที่ไม่มีอยู่ในหน่วยความจำหรือที่คำนวณได้ทันที


1
ฉันถอดการจัดกลุ่มออกและพบว่าฉันสามารถทำให้มันใช้งานได้: from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Boggin

1
คุณยังสามารถซ้อนไวยากรณ์แลมบ์ดาได้: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch

15
@LandonPoch: นั่นไม่ใช่ความคิดที่ดีเพราะจะคำนวณ N ครั้งสูงสุดโดย N เป็นจำนวนรายการในtable.
Daniel Hilgarth

2
@Daniel Hilgarth: จับดี! นั่นจะเป็นการคำนวณสูงสุดต่อทุกแถวในตาราง ความผิดฉันเอง.
Landon Poch


13

คุณสามารถจัดกลุ่มตามสถานะและเลือกแถวจากกลุ่มที่ใหญ่ที่สุด:

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

กลุ่มแรกFirst()ได้รับกลุ่มแรก (ชุดของแถวที่มีสถานะใหญ่ที่สุด) ที่สองFirst()ได้แถวแรกในกลุ่มนั้น
ถ้าสถานะอยู่เสมอ unqiue คุณสามารถแทนที่สองด้วยFirst()Single()


7

ตอบคำถามแรกหากคุณต้องการใช้หลายแถวที่จัดกลุ่มตามเกณฑ์ที่กำหนดกับคอลัมน์อื่นที่มีค่าสูงสุดคุณสามารถทำสิ่งนี้ได้:

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

อีกหนึ่งตัวอย่าง:

ติดตาม:

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

เท่ากับ:

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

-1

เพียงแค่บรรทัดเดียว:

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

สังเกตว่ามีสองการกระทำ การดำเนินการภายในมีไว้สำหรับการค้นหาค่าสูงสุดการดำเนินการภายนอกคือการรับวัตถุที่ต้องการ


วิธีนี้ถูกกล่าวถึงในความคิดเห็นของคำตอบที่ได้รับการยอมรับซึ่งชี้ให้เห็นว่าเป็นความคิดที่ไม่ดี
Boggin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.