Cómo Ordenar una Lista por una propiedad en el objeto


Tengo una clase llamada Order, que tiene propiedades tales como OrderId, OrderDate, Quantity, y Total. Tengo una lista de esta clase Order:

List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders

Ahora quiero ordenar la lista en función de una propiedad del objeto Order, por ejemplo, necesito ordenarla por la fecha de la orden o el id de la orden.

¿Cómo puedo hacer esto en C#?

Author: poke, 2010-07-22

19 answers

La forma más fácil que se me ocurre es usar Linq:

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
 1376
Author: Lazarus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-20 20:54:49

Si necesita ordenar la lista en su lugar, puede usar el Sort método, pasando una Comparison<T> delegado:

objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

Si prefiere crear una nueva secuencia ordenada en lugar de ordenar en el lugar, puede usar la secuencia de LINQ OrderBy método, como se mencionó en las otras respuestas.

 527
Author: LukeH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-22 13:28:33

Para hacer esto sin LINQ en.Net2. 0:

List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
    delegate(Order p1, Order p2)
    {
        return p1.OrderDate.CompareTo(p2.OrderDate);
    }
);

Si estás en .Net3.0, entonces la respuesta de LukeH es lo que buscas.

Para ordenar en varias propiedades, todavía puede hacerlo dentro de un delegado. Por ejemplo:

orderList.Sort(
    delegate(Order p1, Order p2)
    {
        int compareDate = p1.Date.CompareTo(p2.Date);
        if (compareDate == 0)
        {
            return p2.OrderID.CompareTo(p1.OrderID);
        }
        return compareDate;
    }
);

Esto te daría fechas ascendentescon ordenidas descendentes.

Sin embargo, no recomendaría pegar delegados, ya que significará muchos lugares sin reutilización de código. Debe implementar un IComparer y simplemente pasar eso a través de su método Sort. Véase aquí.

public class MyOrderingClass : IComparer<Order>
{
    public int Compare(Order x, Order y)
    {
        int compareDate = x.Date.CompareTo(y.Date);
        if (compareDate == 0)
        {
            return x.OrderID.CompareTo(y.OrderID);
        }
        return compareDate;
    }
}

Y luego para usar esta clase IComparer, simplemente instanciala y pásala a tu método Sort:

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
 202
Author: GenericTypeTea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:29

La forma más sencilla de ordenar una lista es usar OrderBy

 List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ToList();

Si desea ordenar por varias columnas como la siguiente consulta SQL.

ORDER BY OrderDate, OrderId

Para lograr esto puedes usar ThenBy como sigue.

  List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
 79
Author: PSK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 07:41:39

Hacerlo sin Linq como dijiste:

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

Entonces solo llama .sort() en su lista de Pedidos

 31
Author: Jimmy Hoffa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-30 16:36:40

Una Solución Clásica Orientada a Objetos

Primero debo arrodillarme ante la genuflexión de LINQ.... Ahora que tenemos que fuera del camino

Una variación de la respuesta de JimmyHoffa. Con genéricos el parámetro CompareTo se convierte en tipo seguro.

public class Order : IComparable<Order> {

    public int CompareTo( Order that ) {
        if ( that == null ) return 1;
        if ( this.OrderDate > that.OrderDate) return 1;
        if ( this.OrderDate < that.OrderDate) return -1;
        return 0;
    }
}

// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort(); 

Esta capacidad de clasificación predeterminada es reutilizable, por supuesto. Es decir, cada cliente no tiene que reescribir redundantemente la lógica de ordenación. Intercambiar el "1" y " -1 " (o los operadores lógicos, su elección) invierte el orden de clasificación.

 21
Author: radarbob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-11 05:41:48

/ / Clasificación totalmente genérica para usar con un gridview

public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
    {

        List<T> data_sorted = new List<T>();

        if (sortDirection == "Ascending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) ascending
                              select n).ToList();
        }
        else if (sortDirection == "Descending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) descending
                              select n).ToList();

        }

        return data_sorted;

    }

    public object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }
 15
Author: roger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-13 01:37:08

Aquí hay un método genérico de extensión LINQ que no crea una copia adicional de la lista:

public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
    where U : IComparable<U>
{
    list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
}

Para usarlo:

myList.Sort(x=> x.myProperty);

Recientemente construí este adicional que acepta un ICompare<U>, para que pueda personalizar la comparación. Esto me resultó útil cuando necesitaba hacer una ordenación natural de cadenas:

public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
    where U : IComparable<U>
{    
    list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
}
 5
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-08 21:06:09

Usando LINQ

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderDate)
                   .ToList();

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
 3
Author: Daniel A. White,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-22 13:16:27
//Get data from database, then sort list by staff name:

List<StaffMember> staffList = staffHandler.GetStaffMembers();

var sortedList = from staffmember in staffList
                 orderby staffmember.Name ascending
                 select staffmember;
 3
Author: Waqas Ahmed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-23 21:51:44

Una mejora de la versión de Roger.

El problema con GetDynamicSortProperty es que solo obtiene los nombres de las propiedades, pero ¿qué sucede si en GridView usamos NavigationProperties? enviará una excepción, ya que encuentra null.

Ejemplo:

"Employee.Company.Name;" will crash... since permite solo "Name" como parámetro para obtener su valor.

Esta es una versión mejorada que nos permite ordenar por Propiedades de navegación.

public object GetDynamicSortProperty(object item, string propName)
    {
        try
        {                 
            string[] prop = propName.Split('.'); 

            //Use reflection to get order type                   
            int i = 0;                    
            while (i < prop.Count())
            {
                item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
                i++;
            }                     

            return item;
        }
        catch (Exception ex)
        {
            throw ex;
        }


    } 
 3
Author: user1013375,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-04 14:32:52

Puede hacer algo más genérico sobre la selección de propiedades, pero sea específico sobre el tipo que está seleccionando, en su caso 'Order':

Escribe tu función como genérica:

public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
        {
            return (from order in orders
                    orderby propertySelector(order)
                    select order).ToList();
        } 

Y luego úsalo así:

var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);

Puede ser aún más genérico y definir un tipo abierto para lo que desea ordenar:

public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
        {
            return (from item in collection
                    orderby propertySelector(item)
                    select item).ToList();
        } 

Y úsalo de la misma manera:

var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);

Que es una forma compleja e innecesaria de hacer un estilo LINQ 'OrderBy', Pero puede darte un pista de cómo se puede implementar de manera genérica

 3
Author: Danny Mor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-02 10:26:08

Por favor, permítanme completar la respuesta de @ LukeH con un código de ejemplo, ya que lo he probado Creo que puede ser útil para algunos:

public class Order
{
    public string OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public int Quantity { get; set; }
    public int Total { get; set; }

    public Order(string orderId, DateTime orderDate, int quantity, int total)
    {
        OrderId = orderId;
        OrderDate = orderDate;
        Quantity = quantity;
        Total = total;
    }
}

public void SampleDataAndTest()
{
    List<Order> objListOrder = new List<Order>();

    objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
    objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
    objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
    objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
    objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
    objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));


    Console.WriteLine("Sort the list by date ascending:");
    objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by date descending:");
    objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by OrderId ascending:");
    objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    //etc ...
}
 3
Author: molbalga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-19 10:18:06

Ninguna de las respuestas anteriores eran lo suficientemente genéricas para mí, así que hice esta:

var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
var SortedData = DataToBeSorted
                   .OrderBy(m => m.GetType()
                                  .GetProperties()
                                  .First(n => 
                                      n.Name == someUserInputStringValue)
                   .GetValue(m, null))
                 .ToList();

Cuidado con los conjuntos de datos masivos. Es easy code, pero podría meterte en problemas si la colección es enorme y el tipo de objeto de la colección tiene un gran número de campos. El tiempo de ejecución es NxM donde:

N = # de elementos en la colección

M = # de propiedades dentro del objeto

 2
Author: itcropper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-22 18:45:36
var obj = db.Items.Where...

var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
 2
Author: Jevgenij Kononov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-31 08:26:36

Hacer uso de LiNQ OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
 1
Author: Pranay Rana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-22 13:16:31

Basado en el comparador de GenericTypeTea:
podemos obtener más flexibilidad agregando banderas de clasificación:

public class MyOrderingClass : IComparer<Order> {  
    public int Compare(Order x, Order y) {  
        int compareDate = x.Date.CompareTo(y.Date);  
        if (compareDate == 0) {  
            int compareOrderId = x.OrderID.CompareTo(y.OrderID);  

            if (OrderIdDescending) {  
                compareOrderId = -compareOrderId;  
            }  
            return compareOrderId;  
        }  

        if (DateDescending) {  
            compareDate = -compareDate;  
        }  
        return compareDate;  
    }  

    public bool DateDescending { get; set; }  
    public bool OrderIdDescending { get; set; }  
}  

En este escenario, debe crear una instancia como MyOrderingClass explícitamente( en lugar de IComparer )
para establecer sus propiedades de ordenación :

MyOrderingClass comparer = new MyOrderingClass();  
comparer.DateDescending = ...;  
comparer.OrderIdDescending = ...;  
orderList.Sort(comparer);  
 1
Author: Jack Griffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-06 14:05:49

Desde el punto de vista del rendimiento, lo mejor es usar una lista ordenada para que los datos se ordenen a medida que se agregan al resultado. Otros enfoques necesitan al menos una iteración adicional en los datos y la mayoría crean una copia de los datos, por lo que no solo el rendimiento sino el uso de memoria también se verán afectados. Puede que no sea un problema con un par de cientos de elementos, pero lo será con miles, especialmente en servicios donde muchas solicitudes simultáneas pueden ordenar al mismo tiempo. Echa un vistazo al Sistema.Colecciones.Espacio de nombres genérico y elija una clase con clasificación en lugar de Lista.

Y evitar implementaciones genéricas usando reflexión cuando sea posible, esto también puede causar problemas de rendimiento.

 0
Author: user3285954,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-31 11:53:17

Cualquiera que trabaje con tipos nullables, Value debe usar CompareTo.

objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));

 0
Author: Jude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 15:35:41