orderby - sort list string c#




Como classificar uma lista<T> por uma propriedade no objeto (13)

// Classificação totalmente genérica para uso com um 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);
    }

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

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

Agora quero classificar a lista com base em uma propriedade do objeto 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<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();

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<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 usá-lo:

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

Eu criei recentemente este adicional que aceita um ICompare<U> , 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<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)));
}

Baseado no Comparador do GenericTypeTea :
podemos obter mais flexibilidade adicionando sinalizadores de classificação:

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

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

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

Faça uso do LiNQ OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.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


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

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

Se você está no .Net3.0, então a answer 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 delegados, 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 here

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

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

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);

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

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

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.


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, mas ser específico sobre o tipo que está selecionando, no seu caso 'Ordem':

escreva sua função 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();
        } 

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<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> 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


Uma solução orientada a objetos 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<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(); 

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


var obj = db.Items.Where...

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




sorting