Cómo ordenar una lista por una propiedad en el objeto

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

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

Ahora quiero ordenar la lista en función de una propiedad del objeto Order , por ejemplo, debo ordenarla por la fecha del pedido o por el ID del pedido.

¿Cómo puedo hacer esto en C #?

La forma más fácil que puedo pensar es usar Linq:

 List SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList(); 

Si necesita ordenar la lista in situ, puede usar el método Sort pasando un delegado de Comparison :

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

Si prefiere crear una secuencia nueva y ordenada en lugar de organizarla en el lugar, puede usar el método OrderBy de LINQ, como se menciona en las otras respuestas.

Para hacer esto sin LINQ en .Net2.0:

 List 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 múltiples propiedades, 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 le daría fechas ascendentes con orderDed descendente .

Sin embargo, no recomendaría pegar delegates ya que significará muchos lugares sin reutilización de código. Debe implementar un IComparer y simplemente pasarlo a su método Sort . Mira aquí .

 public class MyOrderingClass : IComparer { 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 de IComparer, simplemente ejemplínelo y páselo a su método Sort:

 IComparer comparer = new MyOrderingClass(); orderList.Sort(comparer); 

La forma más simple de pedir una lista es usar OrderBy

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

Si desea realizar un pedido por varias columnas, como la siguiente consulta SQL.

 ORDER BY OrderDate, OrderId 

Para lograr esto, puede usar ThenBy como sigue.

  List objListOrder = source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList(); 

Haciéndolo 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; } } 

Luego solo llame a .sort () en su lista de pedidos

Una solución orientada a objetos clásica

Primero debo arrodillarme ante la genialidad de LINQ … Ahora que lo tenemos todo fuera del camino

Una variación en la respuesta de JimmyHoffa. Con generics, el parámetro CompareTo se convierte en seguro para tipos.

 public class Order : IComparable { 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 myOrders.Sort(); 

Esta clasificación predeterminada es reutilizable, por supuesto. Es decir, cada cliente no tiene que volver a escribir de forma redundante la lógica de clasificación. Al intercambiar el “1” y “-1” (o los operadores lógicos, su elección) invierte el orden de clasificación.

// Clasificación totalmente genérica para usar con una vista de grilla

 public List Sort_List(string sortDirection, string sortExpression, List data) { List data_sorted = new List(); 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); } 

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

 public static void Sort(this List list, Func expression) where U : IComparable { 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 , para que pueda personalizar la comparación. Esto fue útil cuando tuve que hacer una secuencia de cuerda natural:

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

Usando LINQ

 objListOrder = GetOrderList() .OrderBy(o => o.OrderDate) .ToList(); objListOrder = GetOrderList() .OrderBy(o => o.OrderId) .ToList(); 
 //Get data from database, then sort list by staff name: List staffList = staffHandler.GetStaffMembers(); var sortedList = from staffmember in staffList orderby staffmember.Name ascending select staffmember; 

Una versión mejorada de Roger.

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

Ejemplo:

“Employee.Company.Name;” se bloqueará … ya que solo permite que “Name” como parámetro obtenga su valor.

Aquí hay 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; } } 

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

escriba su función como una genérica:

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

y luego usarlo 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 OrderBy(IEnumerable collection, Func propertySelector) { return (from item in collection orderby propertySelector(item) select item).ToList(); } 

y usarlo de la misma manera:

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

Que es una manera estúpida, innecesaria y compleja de hacer un ‘LINK’ de LINQ, pero puede darte una pista de cómo se puede implementar de una manera genérica

Permítanme completar la respuesta por @LukeH con un código de muestra, ya que lo he probado y 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 objListOrder = new List(); 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 ... } 

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

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

Sin embargo, cuidado con los conjuntos de datos masivos. Es un código fácil pero podría generar problemas si la colección es enorme y el tipo de objeto de la colección tiene una gran cantidad de campos. El tiempo de ejecución es NxM donde:

N = # de elementos en la colección

M = # de propiedades dentro del objeto

 var obj = db.Items.Where... var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID)); 

Hacer uso de LiNQ OrderBy

 List objListOrder=new List (); objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList(); 

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

 public class MyOrderingClass : IComparer { 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 instanciarlo como MyOrderingClass explícitamente (en lugar de IComparer )
para establecer sus propiedades de clasificación:

 MyOrderingClass comparer = new MyOrderingClass(); comparer.DateDescending = ...; comparer.OrderIdDescending = ...; orderList.Sort(comparer); 

Desde el punto de vista del rendimiento, lo mejor es usar una lista ordenada para que los datos se clasifiquen como se agreguen al resultado. Otros enfoques necesitan al menos una iteración adicional en los datos y la mayoría crean una copia de los datos, de modo que no solo el rendimiento sino también el uso de la memoria se verán afectados. Puede que no sea un problema con un par de cientos de elementos, sino que será con miles, especialmente en servicios donde muchas solicitudes concurrentes pueden ordenar al mismo tiempo. Eche un vistazo al espacio de nombres System.Collections.Generic y elija una clase con clasificación en lugar de List.

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

Cualquiera que trabaje con tipos anulables, se requiere un valor para usar CompareTo .

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