Jak posortować listę według właściwości w obiekcie

Mam klasę {[1] } która ma takie właściwości jak OrderId, OrderDate, Quantity, i Total. Mam listę tej klasy Order:

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

Teraz chcę posortować listę według jednej właściwości obiektu Order, na przykład muszę posortować ją według daty zamówienia lub ID zamówienia.

Jak mogę to zrobić w C#?

Author: poke, 2010-07-22

19 answers

Najprostszym sposobem, jaki przychodzi mi do głowy, jest użycie 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 20:54:49

Jeśli chcesz posortować listę na miejscu, możesz użyć Sort metoda, zdając Comparison<T> delegat:

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

Jeśli wolisz utworzyć nową, posortowaną sekwencję zamiast sortować na miejscu, możesz użyć LINQ ' a OrderBy metoda, jak wspomniano w innych odpowiedziach.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-22 13:28:33

Aby zrobić to bez LINQ na. Net2. 0:

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

Jeśli jesteś na .Net3.0, to odpowiedź LukeH jest tym, czego szukasz.

Aby sortować na wielu właściwościach, możesz to zrobić w obrębie delegata. Na przykład:

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

To daje rosnąco daty z malejąco kolejność.

Jednak nie polecam przyklejania delegatów, ponieważ będzie to oznaczać wiele miejsc bez ponownego użycia kodu. Powinieneś zaimplementować IComparer i po prostu przekazać do twojej metody Sort. Zobacz tutaj .

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

A następnie, aby użyć tej klasy IComparer, wystarczy utworzyć instancję i przekazać ją do metody sortowania:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:29

Najprostszym sposobem uporządkowania listy jest użycie OrderBy

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

Jeśli chcesz zamówić przez wiele kolumn, takich jak następujące zapytanie SQL.

ORDER BY OrderDate, OrderId

Aby to osiągnąć, możesz użyć ThenBy Jak po.

  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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 07:41:39

Robienie tego bez Linq tak jak powiedziałeś:

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;
    }
}
Więc zadzwoń .sort () na liście zleceń
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-30 16:36:40

Klasyczne Rozwiązanie Obiektowe

Najpierw muszę pochwalić się niesamowitością LINQA.... Teraz, gdy mamy to z drogi Wariacja na temat odpowiedzi JimmyHoffa. W przypadku generycznych parametr CompareTo staje się bezpieczny dla typu.
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(); 

Ta domyślna sortowalność jest oczywiście możliwa do ponownego użycia. Oznacza to, że każdy klient nie musi redundantnie przepisywać logiki sortowania. Zamiana "1" i " -1 " (lub operatorów logicznych, wybór) odwraca kolejność sortowania.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-11 05:41:48

// Totally generic sorting for use with a 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-13 01:37:08

Oto ogólna metoda rozszerzenia LINQ, która nie tworzy dodatkowej kopii listy:

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

Aby go użyć:

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

Niedawno zbudowałem ten dodatkowy, który akceptuje ICompare<U>, abyś mógł dostosować porównanie. To przydało mi się, gdy musiałem zrobić naturalny sortowanie strun:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-08 21:06:09

Using 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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-23 21:51:44

Ulepszona wersja Rogera.

Problem z GetDynamicSortProperty polega na tym, że pobieramy tylko nazwy właściwości, ale co się stanie, jeśli w GridView użyjemy NavigationProperties? wyśle wyjątek, ponieważ znajdzie null.

Przykład:

"Employee.Company.Name;" will crash... ponieważ pozwala tylko" nazwa " jako parametr, aby uzyskać jego wartość.

Oto ulepszona wersja, która pozwala nam sortować według właściwości nawigacji.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-04 14:32:52

Możesz zrobić coś bardziej ogólnego o wyborze właściwości, ale być konkretnym o typie, z którego wybierasz, w Twoim przypadku 'Order':

Napisz swoją funkcję jako ogólną:

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

A następnie użyj go w ten sposób:

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

Możesz być jeszcze bardziej ogólny i zdefiniować typ otwarty dla tego, co chcesz zamówić:

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

I użyj go w ten sam sposób:

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

Co jest głupim niepotrzebnym skomplikowanym sposobem robienia stylu LINQ 'OrderBy', Ale to może dać ci podpowiedź, w jaki sposób można ją zaimplementować w sposób ogólny

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-02 10:26:08

Proszę, pozwól mi uzupełnić odpowiedź @LukeH przykładowym kodem, ponieważ Przetestowałem go uważam, że może być przydatny dla niektórych:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-19 10:18:06

Żadna z powyższych odpowiedzi nie była dla mnie wystarczająco ogólna, więc zrobiłem tę:

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

Uważaj jednak na ogromne zbiory danych. Jest to łatwy kod, ale może mieć kłopoty, jeśli kolekcja jest ogromna, a typ obiektu kolekcji ma dużą liczbę pól. Czas wykonania to NxM gdzie:

N = # elementów w zbiorze

M = # właściwości wewnątrz obiektu

 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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 08:26:36

Użyj 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-22 13:16:31

Na podstawie GenericTypeTea 'S Comparer:
możemy uzyskać większą elastyczność, dodając flagi sortowania:

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

W tym scenariuszu musisz utworzyć instancję jako MyOrderingClass jawnie (zamiast IComparer)
w celu ustawienia jego właściwości sortowania:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-06 14:05:49

Z punktu widzenia wydajności najlepiej jest użyć posortowanej listy, aby dane były sortowane w miarę dodawania do wyniku. Inne podejścia wymagają co najmniej jednej dodatkowej iteracji danych, a większość z nich tworzy kopię danych, co wpływa nie tylko na wydajność, ale i zużycie pamięci. Może to nie być problem z kilkoma setkami elementów, ale z tysiącami, zwłaszcza w usługach, w których wiele jednoczesnych żądań może sortować w tym samym czasie. Spójrz na System.Kolekcje.Generic namespace i wybierz klasę z sortowaniem zamiast listy.

I unikaj ogólnych implementacji wykorzystujących odbicie, jeśli to możliwe, może to również powodować problemy z wydajnością.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-31 11:53:17

Każdy, kto pracuje z typami nullable, Value jest zobowiązany do użycia 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-19 15:35:41