LINQ Использование Max () для выбора одной строки

95

Я использую 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

Это неверно, но я не могу придумать правильную форму.

Кстати, на самом деле я пытаюсь достичь примерно следующего:

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 02
@ M.Babcock, довольно далеко в этом вопросе был хороший ответ: stackoverflow.com/a/6330485/444244
Boggin
Есть гораздо лучшие, чем это ...
Бэбкок
Взгляните на ответ .
Сергей Брунов
@Serge Я согласен, что morelinq было бы лучше всего, но я боюсь, что в этом проекте есть препятствия для добавления новых библиотек.
Boggin 02

Ответы:

233

Я не понимаю, почему вы здесь группируетесь.

Попробуй это:

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
Боггин
1
Вы также можете вложить лямбда-синтаксис: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch
13
@LandonPoch: Это не очень хорошая идея, так как это вычислит максимальное количество раз N, где N - количество элементов в table.
Дэниел Хилгарт
2
@ Даниэль Хилгарт: Хороший улов! Фактически это будет вычислять максимум для каждой строки в таблице. Виноват.
Landon Poch
17

Вы также можете:

(from u in table
orderby u.Status descending
select u).Take(1);
КАПИЛ ШАРМА
источник
13

Вы можете сгруппировать по статусу и выбрать строку из самой большой группы:

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

Первый First()получает первую группу (набор строк с наибольшим статусом); второй First()получает первую строку в этой группе.
Если статус всегда unqiue, вы можете заменить второй First()на Single().

SLaks
источник
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)
SantanaFire
источник
-1

Просто в одной строке:

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

Обратите внимание, что есть два действия. внутреннее действие предназначено для поиска максимального значения, внешнее действие - для получения желаемого объекта.

Шнеор
источник
Этот метод обсуждался в комментариях к принятому ответу, где было указано, что это плохая идея.
Боггин,