Comment Trier une Liste par une propriété de l'objet

j'ai une classe appelée Order qui a des propriétés telles que OrderId , OrderDate , Quantity , et Total . J'ai une liste de cette classe Order :

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

maintenant je veux trier la liste basée sur une propriété de l'objet Order , par exemple je dois le Trier par la date d'ordre ou l'id d'ordre.

Comment puis-je faire cela dans C#?

936
demandé sur poke 2010-07-22 17:13:25

19 réponses

la manière la plus simple que je puisse imaginer est D'utiliser Linq:

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
1376
répondu Lazarus 2015-03-20 20:54:49

si vous devez trier la liste en place, vous pouvez utiliser la méthode Sort , en passant par un Comparison<T> délégué:

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

si vous préférez créer une nouvelle séquence triée plutôt qu'un tri en place, vous pouvez utiliser la méthode OrderBy de LINQ, comme mentionné dans les autres réponses.

527
répondu LukeH 2010-07-22 13:28:33

pour le faire sans poser de questions .Net2.0:

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

si vous êtes à l'antenne .Net3.0, puis LukeH réponse est ce que vous cherchez.

Pour trier sur plusieurs propriétés, vous pouvez toujours le faire à l'intérieur d'un délégué. Par exemple:

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

cela vous donnerait Ascendant dates avec descendant orderides.

Cependant, I Je ne recommande pas de coller des délégués car cela signifiera beaucoup d'endroits sans code réutiliser. Vous devez implémenter un IComparer et passer simplement cela à votre méthode Sort . Voir ici .

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

et ensuite pour utiliser cette classe IComparer, instanciez-la et passez-la à votre méthode de tri:

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
202
répondu GenericTypeTea 2017-05-23 11:47:29

la façon la plus Simple de commander une liste est d'utiliser OrderBy

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

si vous voulez commander par plusieurs colonnes comme la requête SQL suivante.

ORDER BY OrderDate, OrderId

pour ce faire, vous pouvez utiliser ThenBy comme suit.

  List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
79
répondu PSK 2018-06-13 07:41:39

de le Faire sans Linq comme vous l'avez dit:

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

alors appelez .sort () sur votre liste d'ordres

31
répondu Jimmy Hoffa 2014-01-30 16:36:40

Un Objet Classique Orientée Solution

tout d'abord, je dois faire la génuflexion à la splendeur de LINQ.... Maintenant que c'est fait

Une variation sur JimmyHoffa réponse. Avec generics, le paramètre CompareTo devient type safe.

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

cette sortabilité par défaut est bien sûr réutilisable. C'est-à-dire que chaque client n'a pas à réécrire de manière redondante la logique de tri. Échanger le" 1 "et le" -1 " (ou les opérateurs logiques, votre choix) inverse l'ordre de tri.

21
répondu radarbob 2017-02-11 05:41:48

// Totalement générique de tri pour une utilisation avec 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);
    }
15
répondu roger 2013-06-13 01:37:08

Voici une méthode D'extension LINQ générique qui ne crée pas de copie supplémentaire de la liste:

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

pour l'utiliser:

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

j'ai récemment construit celui-ci supplémentaire qui accepte un ICompare<U> , de sorte que vous pouvez personnaliser la comparaison. Cela m'a été utile lorsque j'ai eu besoin de faire un tri des cordes naturelles:

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
répondu Peter 2014-04-08 21:06:09

utilisant LINQ

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

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
3
répondu Daniel A. White 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
répondu Waqas Ahmed 2011-11-23 21:51:44

une version améliorée de celle de Roger.

le problème avec GetDynamicSortProperty est que seulement obtenir les noms de propriétés, mais qu'arrive-t-il si dans le GridView nous utilisons NavigationProperties? il enverra une exception, car il trouve nulle.

exemple:

" employé.De la compagnie.Nom; " s'écrasera... depuis permet seulement "Nom" comme paramètre pour obtenir sa valeur.

Voici une version améliorée qui nous permet de trier par Propriétés de Navigation.

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
répondu user1013375 2013-07-04 14:32:52

vous pouvez faire quelque chose de plus générique au sujet de la sélection de propriétés tout en étant précis sur le type que vous choisissez, dans votre cas 'Order':

écrivez votre fonction comme une fonction générique:

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

et puis l'utiliser comme ceci:

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

vous pouvez être encore plus générique et définir un type ouvert pour ce que vous voulez commander:

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

et utilisez-le de la même façon:

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

qui est une façon complexe stupide de faire un style LINQ 'OrderBy', Mais il peut vous donner une idée de la façon dont il peut être mis en œuvre de manière générique

3
répondu Danny Mor 2015-04-02 10:26:08

s'il vous plaît laissez-moi compléter la réponse par @LukeH avec un code échantillon, comme je l'ai testé je crois qu'il peut être utile pour certains:

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
répondu molbalga 2016-07-19 10:18:06

aucune des réponses ci-dessus n'était assez générique pour moi donc j'ai fait celle-ci:

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

prudent sur les ensembles de données massives cependant. C'est du code facile mais pourrait vous mettre en difficulté si la collection est énorme et le type d'objet de la collection a un grand nombre de champs. Le temps d'exécution est NxM où:

N = nombre d'éléments dans la collection

M = nombre de propriétés de l'objet

2
répondu itcropper 2016-08-22 18:45:36
var obj = db.Items.Where...

var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
2
répondu Jevgenij Kononov 2017-05-31 08:26:36

faites usage de LiNQ OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
1
répondu Pranay Rana 2010-07-22 13:16:31

Basé sur GenericTypeTea Comparer :

nous pouvons obtenir plus de flexibilité en ajoutant des indicateurs de tri :

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

dans ce scénario, vous devez l'instancier comme MyOrderingClass explicitement (plutôt que IComparer )

afin de régler ses propriétés de tri:

MyOrderingClass comparer = new MyOrderingClass();  
comparer.DateDescending = ...;  
comparer.OrderIdDescending = ...;  
orderList.Sort(comparer);  
1
répondu Jack Griffin 2015-01-06 14:05:49

du point de vue des performances, le mieux est d'utiliser une liste triée pour que les données soient triées comme elles sont ajoutées au résultat. D'autres approches nécessitent au moins une itération supplémentaire sur les données et la plupart créent une copie de données donc non seulement la performance mais l'utilisation de la mémoire sera affectée aussi. Peut-être ne pas être un problème avec quelques centaines d'éléments, mais sera avec des milliers, en particulier dans les services où de nombreuses demandes concurrentes peuvent faire le tri en même temps. Jetez un oeil à Système.Collection.Générique namespace et choisissez une classe avec tri au lieu de List.

et éviter les implémentations génériques en utilisant la réflexion lorsque cela est possible, cela peut aussi causer des problèmes de performance.

0
répondu user3285954 2014-07-31 11:53:17

toute personne travaillant avec des types non utilisables, Value est tenue d'utiliser CompareTo .

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

0
répondu Jude 2017-01-19 15:35:41