c# set - Come ordinare una lista<T>da una proprietà nell'oggetto




automatico property (17)

Ho una classe chiamata Order che ha proprietà come OrderId , OrderId , Quantity e Total . Ho una lista di questa classe di Order :

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

Ora voglio ordinare l'elenco in base a una proprietà dell'oggetto Order , ad esempio ho bisogno di ordinarlo in base alla data dell'ordine o all'ID ordine.

Come posso farlo in C #?


Answers

Dal punto di vista delle prestazioni, il migliore è utilizzare una lista ordinata in modo che i dati vengano ordinati in base al risultato. Altri approcci richiedono almeno un'iterazione aggiuntiva sui dati e la maggior parte crea una copia dei dati, quindi non solo le prestazioni, ma anche l'utilizzo della memoria ne risentirà. Potrebbe non essere un problema con un paio di centinaia di elementi, ma sarà con migliaia, soprattutto nei servizi in cui molte richieste simultanee possono fare lo smistamento allo stesso tempo. Dai uno sguardo allo spazio dei nomi System.Collections.Generic e scegli una classe con l'ordinamento invece di Elenco.

Evitare implementazioni generiche utilizzando la riflessione quando possibile, questo può causare anche problemi di prestazioni.


Il modo più semplice a cui riesco a pensare è usare Linq:

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

Per favore lasciatemi completare la risposta di @LukeH con un codice di esempio, poiché l'ho provato credo che possa essere utile per alcuni:

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

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

Chiunque lavori con i tipi nullable, è necessario un valore per utilizzare CompareTo .

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


Il modo più semplice per ordinare un elenco è utilizzare OrderBy

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

Se si desidera ordinare per più colonne come seguendo SQL Query.

ORDER BY OrderDate, OrderId

Per raggiungere questo obiettivo, puoi utilizzare ThenBy come segue.

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

Per farlo senza LINQ su .Net2.0:

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

Se sei su .Net3.0, la answer di LukeH è ciò che cerchi.

Per ordinare su più proprietà, puoi ancora farlo all'interno di un delegato. Per esempio:

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

Questo ti darebbe date ascendenti con ordini decrescenti .

Tuttavia, non consiglierei di attaccare i delegati poiché significherebbe molti posti senza riutilizzo del codice. Dovresti implementare un IComparer e trasferirlo nel tuo metodo Sort . Vedi 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;
    }
}

Quindi, per utilizzare questa classe IComparer, basta istanziarla e passarla al metodo Sort:

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

Una soluzione classica orientata agli oggetti

Per prima cosa devo genuflessi alla bellezza di LINQ ... Ora che ce l'abbiamo tolta

Una variazione sulla risposta di JimmyHoffa. Con i generici il parametro CompareTo diventa sicuro.

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

Questa sorta di default è ovviamente riutilizzabile. Questo è il motivo per cui ogni cliente non deve riscrivere in modo ridondante la logica di ordinamento. Scambiando "1" e "-1" (o gli operatori logici, a tua scelta) si inverte l'ordinamento.


Usa LiNQ OrderBy

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

Se è necessario ordinare l'elenco sul posto, è possibile utilizzare il metodo Sort , passando un delegato di Comparison<T> :

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

Se si preferisce creare una sequenza nuova, ordinata piuttosto che ordinare sul posto, è possibile utilizzare il metodo OrderBy di LINQ, come menzionato nelle altre risposte.


Farlo senza Linq come hai detto:

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

Quindi chiama semplicemente .sort () nella tua lista di ordini


Basato sul Comparatore di GenericTypeTea :
possiamo ottenere più flessibilità aggiungendo flag di ordinamento:

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

In questo scenario, è necessario istanziarlo come MyOrderingClass in modo esplicito (piuttosto che IComparer )
per impostare le sue proprietà di classificazione:

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

Nessuna delle risposte di cui sopra è stata abbastanza generica per me, quindi ho fatto questo:

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

Attento su enormi set di dati però. È un codice facile ma potrebbe metterti nei guai se la collezione è enorme e il tipo di oggetto della raccolta ha un numero elevato di campi. Il tempo di esecuzione è NxM dove:

N = numero di elementi nella collezione

M = numero di proprietà all'interno dell'oggetto


Utilizzando LINQ

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

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

Un miglioramento della versione di Roger.

Il problema con GetDynamicSortProperty è che ottengono solo i nomi delle proprietà ma cosa succede se nel GridView usiamo NavigationProperties? invierà un'eccezione, poiché trova nulla.

Esempio:

"Employee.Company.Name;" si arresta in modo anomalo ... poiché consente solo il "Nome" come parametro per ottenere il suo valore.

Ecco una versione migliorata che ci consente di ordinare per Proprietà di navigazione.

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


    } 

// Ordinamento completamente generico da utilizzare 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);
    }

La cosa migliore è implementare un metodo di estensione come

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

e quindi utilizzarlo ovunque nella soluzione

var copy = anyObject.DeepClone();

Possiamo avere le seguenti tre implementazioni:

  1. Per serializzazione (il codice più breve)
  2. Con Reflection - 5 volte più veloce
  3. Da Expression Trees - 20 volte più veloce

Tutti i metodi collegati funzionano bene e sono stati profondamente testati.





c# generics list sorting