sábado, 26 de septiembre de 2015

LINQ. Ordenar por múltiples criterios.

Esta es una consulta bastante habitual.

Cuando tenemos una colección de elementos podemos utilizar los métodos de extensión OrderBy y OrderByDescending para ordenarla de forma sencilla por alguna de sus propiedades pero, ¿qué sucede cuando el criterio de ordenación es múltiple?


Para el ejemplo vamos a suponer que tenemos una variable Personas  que es una lista genérica de instancias de la clase Persona. La cual tiene tres propiedades: Nombre, Apellido y Edad.


class Persona
{
    public string Nombre { get; set; }
    public string Apellido { get; set; }
    public int Edad { get; set; }
}

...

List<Persona> Personas = new List<Persona>();

Así podríamos ordenar de forma sencilla la lista Personas por la propiedad Apellido utilizando el método de extensión OrderBy.


IEnumerable<Persona> PersonasOrdenadas = Personas.OrderBy(p => p.Apellido);

La cosa se empieza a complicar si lo que deseamos es ordenar la lista por apellido y que, los que tengan el mismo apellido, se ordenen por edad.

Podríamos utilizar otra sobrecarga del método OrderBy que recibe como parámetro un objeto que implementa la interfaz IComparer<T> que contiene la lógica para ordenar los elementos de la lista.

En primer lugar deberemos crear el comparador para la clase Persona que implementa la interfaz IComparer<Persona> y que definirá la lógica para ordenar los elementos por apellido y edad en el método Compare:


class CompareApellidoEdad : IComparer<Persona>
{
    public int Compare(Persona x, Persona y)
    {
        int ComparaPersona = string.Compare(x.Apellido, y.Apellido);
        if (ComparaPersona == 0)
            ComparaPersona = x.Edad.CompareTo(y.Edad);
        return ComparaPersona;
    }
}

Ahora ya podríamos utilizar la nueva sobrecarga del método OrderBy:


IEnumerable<Persona> PersonasOrdenadas = Personas.OrderBy(p => p, new CompareApellidoEdad());

Demasiado trabajo para algo tan simple ¿verdad?



Por suerte disponemos de otros dos métodos de extensión que nos permiten realizar esta tarea de una forma mucho más sencilla: ThenBy y ThenByDescending.

Estos métodos de extensión se aplican a objetos que implementan la interfaz IOrderedEnumerable, como sucede con el valor devuelto por los métodos OrderBy y OrderByDescending. Los métodos añaden el criterio de ordenación indicado respetando los existentes en el objeto IOrderedEnumerable y devuelve un nuevo objeto IOrderedEnumerable.

De esta forma resulta tremendamente sencillo realizar la ordenación:


IEnumerable<Persona> PersonasOrdenadas = Personas.OrderBy(p => p.Apellido).ThenBy(p => p.Edad);

Dado que, como ya he comentado, los métodos ThenBy y ThenByDescending devuelven también objetos del tipo IOrderedEnumerable, podemos encadenar tantos métodos ThenBy y ThenByDescending como queramos.


IEnumerable<Persona> PersonasOrdenadas = Personas.OrderBy(p => p.Apellido)
    .ThenBy(p => p.Edad)
    .ThenByDescending(p => p.Nombre);

5 comentarios:

  1. Excelente artículo. Me sirvió esto que no encontraba por ningún lado.

    ResponderEliminar
  2. Tengo enlazado un DataGridView y al pulsar cabecaras ,( DataGridView.Columns(e.ColumnIndex)), ordeno como explicas en los ejemplos:

    Select DataGridView.Columns(e.ColumnIndex).name
    case "Codigo"
    DataGridView.DataSource = ColDatosDeOperarios.OrderBy(Function(x) x.Codigo).ToList
    case "Nombre"
    DataGridView.DataSource = ColDatosDeOperarios.OrderBy(Function(x) x.Nombre).ToList
    etc..
    End Select

    como puedo hacerlo directamente, (sin la select), sustituyendo x.Codigo, x.Nombre

    Gracias y un saludo.

    ResponderEliminar