LINQ Usando Max () para seleccionar una sola fila

95

Estoy usando LINQ en un IQueryable devuelto por NHibernate y necesito seleccionar la fila con los valores máximos en un par de campos.

He simplificado la parte en la que me quedo. Necesito seleccionar la fila de mi tabla con el valor máximo en 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

Esto es incorrecto pero no puedo encontrar la forma correcta.

Por cierto, lo que en realidad estoy tratando de lograr es aproximadamente esto:

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

Comencé con la lambda anterior, pero he estado usando LINQPad para intentar resolver la sintaxis para seleccionar Max ().

ACTUALIZAR

Eliminar el GroupBy fue clave.

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
fuente
posible duplicado: stackoverflow.com/questions/1101841/…
M.Babcock
@ M.Babcock hubo una buena respuesta bastante abajo en esa pregunta: stackoverflow.com/a/6330485/444244
Boggin
Hay mucho mejores que ese ...
M.Babcock
Eche un vistazo a la respuesta .
Sergey Brunov
@Serge Estoy de acuerdo en que morelinq sería lo mejor, pero me temo que este proyecto tiene impedimentos para agregar nuevas bibliotecas.
Boggin

Respuestas:

230

No veo por qué se está agrupando aquí.

Prueba esto:

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

Un enfoque alternativo que repetiría table solo una vez sería este:

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

Esto es útil si tablees una IEnumerable<T>que no está presente en la memoria o que se calcula sobre la marcha.

Daniel Hilgarth
fuente
1
from u in User_Accounts where u.Status == User_Accounts.Max(y => y.Status) select u
Saqué
1
También puede anidar la sintaxis lambda: table.First(x => x.Status == table.Max(x => x.Status))
Landon Poch
13
@LandonPoch: Esa no es una buena idea, ya que esto calcularía el máximo de N veces siendo N el número de elementos en table.
Daniel Hilgarth
2
@Daniel Hilgarth: ¡Buen partido! De hecho, eso calcularía el máximo por cada fila de la tabla. Culpa mía.
Landon Poch
17

También puedes hacer:

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

Puede agrupar por estado y seleccionar una fila del grupo más grande:

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

El primero First()obtiene el primer grupo (el conjunto de filas con el estado más grande); el segundo First()obtiene la primera fila de ese grupo.
Si el estado siempre es único, puede reemplazar el segundo First()con Single().

SLaks
fuente
7

Abordando la primera pregunta, si necesita tomar varias filas agrupadas por ciertos criterios con la otra columna con valor máximo, puede hacer algo como esto:

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

Más un ejemplo:

Seguir:

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

Igual a:

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

Simplemente en una línea:

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

Observe que hay dos acciones. la acción interna es para encontrar el valor máximo, la acción externa es para obtener el objeto deseado.

Shneor
fuente
Este método se discutió en los comentarios a la respuesta aceptada donde se señaló que era una mala idea.
Boggin