Como classificar uma lista por uma propriedade no object

Eu tenho uma class chamada Order que possui propriedades como OrderId , OrderDate , Quantity e Total . Eu tenho uma lista desta class Order :

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

Agora quero classificar a lista com base em uma propriedade do object Order , por exemplo, preciso classificá-la pela data do pedido ou pelo ID do pedido.

Como posso fazer isso em c #?

A maneira mais fácil de pensar é usar o Linq:

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

Se você precisar ordenar a lista no local, poderá usar o método Sort , passando um delegado de Comparison :

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

Se você preferir criar uma nova sequência classificada em vez de classificar no local, poderá usar o método OrderBy do LINQ, conforme mencionado nas outras respostas.

Para fazer isso sem o LINQ no .net2.0:

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

Se você está no .Net3.0, então a resposta do LukeH é o que você está procurando.

Para classificar em várias propriedades, você ainda pode fazê-lo em um delegado. Por exemplo:

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

Isso daria a você datas ascendentes com orderIds decrescentes .

No entanto, eu não recomendaria colocar os delegates, pois isso significa muitos lugares sem reutilização de código. Você deve implementar um IComparer e passar isso para o seu método Sort . Veja aqui

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

E então, para usar essa class IComparer, basta instanciá-la e passá-la ao seu método Sort:

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

A maneira mais simples de pedir uma lista é usar OrderBy

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

Se você deseja ordenar por várias colunas, como seguindo a Consulta SQL.

 ORDER BY OrderDate, OrderId 

Para conseguir isso, você pode usar ThenBy como segue.

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

Fazendo isso sem o Linq como você disse:

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

Em seguida, basta chamar .sort () na sua lista de pedidos

Uma solução orientada a objects clássicos

Primeiro eu tenho que fazer uma genuflexão para a grandiosidade do LINQ …. Agora que temos isso fora do caminho

Uma variação na resposta de JimmyHoffa. Com genéricos, o parâmetro CompareTo se torna seguro.

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

Essa sorting padrão é reutilizável, é claro. Ou seja, cada cliente não precisa rewrite redundantemente a lógica de sorting. Trocando o “1” e “-1” (ou os operadores lógicos, sua escolha) inverte a ordem de sorting.

// Classificação totalmente genérica para uso com um gridview

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

Aqui está um método de extensão LINQ genérico que não cria uma cópia extra da 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 usá-lo:

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

Eu criei recentemente este adicional que aceita um ICompare , para que você possa personalizar a comparação. Isso veio a calhar quando eu precisei fazer um tipo de string 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 o 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; 

Uma melhoria da versão de Roger.

O problema com GetDynamicSortProperty é que só obtém os nomes das propriedades, mas o que acontece se no GridView usamos NavigationProperties? Ele irá enviar uma exceção, uma vez que encontra null.

Exemplo:

“Employee.Company.Name;” irá falhar … já que permite apenas “Name” como parâmetro para obter seu valor.

Aqui está uma versão melhorada que nos permite classificar por propriedades de navegação.

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

Você pode fazer algo mais genérico sobre a seleção de propriedades e ser específico sobre o tipo de seleção, no seu caso ‘Ordem’:

escreva sua function como genérica:

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

e depois usá-lo assim:

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

Você pode ser ainda mais genérico e definir um tipo aberto para o que você deseja solicitar:

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

e usá-lo da mesma maneira:

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

O que é uma maneira complexa, desnecessária e estúpida de fazer um estilo LINQ “OrderBy”, mas pode lhe dar uma pista de como ele pode ser implementado de uma forma genérica

Por favor, deixe-me completar a resposta por @LukeH com algum código de exemplo, como testei, acredito que possa ser útil para alguns:

 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 ... } 

Nenhuma das respostas acima foi genérica o suficiente para mim, então eu fiz 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(); 

Cuidado com conjuntos de dados massivos. É um código fácil, mas pode causar problemas se a coleção for grande e o tipo de object da coleção tiver um grande número de campos. O tempo de execução é NxM, em que:

N = # de elementos na coleção

M = # de propriedades dentro do object

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

Faça uso do LiNQ OrderBy

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

Baseado no Comparador do GenericTypeTea :
podemos obter mais flexibilidade adicionando sinalizadores de sorting:

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

Nesse cenário, você deve instanciá-lo como MyOrderingClass explicitamente (em vez disso IComparer )
para definir suas propriedades de sorting:

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

Do ponto de vista do desempenho, o melhor é usar uma lista classificada para que os dados sejam classificados à medida que são adicionados ao resultado. Outras abordagens precisam de pelo menos uma iteração extra nos dados e a maioria cria uma cópia dos dados, portanto, não apenas o desempenho, mas o uso da memory também serão afetados. Pode não ser um problema com algumas centenas de elementos, mas será com milhares, especialmente em serviços onde muitas solicitações simultâneas podem fazer a sorting ao mesmo tempo. Dê uma olhada no System.Collections.Generic namespace e escolha uma class com sorting em vez de List.

E evitar implementações genéricas usando reflection quando possível, isso pode causar problemas de desempenho também.

Qualquer pessoa que trabalhe com tipos anuláveis, o Value é necessário para usar CompareTo .

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