1496 votes

Comment trier une Liste<T> par une propriété de l'objet

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

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

Je veux trier la liste en fonction d'une propriété de l'élément Order par exemple, soit par la date de la commande, soit par l'identifiant de la commande.

Comment puis-je faire cela en C# ?

2119voto

Lazarus Points 17526

Le moyen le plus simple auquel je pense est d'utiliser Linq :

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

28 votes

Comment puis-je le trier dans l'ordre décroissant.

259 votes

@BonusKun List<Order> SortedList = objListOrder. OrderByDescending (o=>o.OrderDate).ToList() ;

91 votes

Notez que cela crée une toute nouvelle liste avec tous les éléments en mémoire, qui mai être problématique en termes de performances.

1023voto

LukeH Points 110965

Si vous avez besoin de trier la liste sur place, vous pouvez utiliser la fonction Sort en passant 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 que de trier sur place, vous pouvez utiliser la fonction LINQ OrderBy comme indiqué dans les autres réponses.

1 votes

Si vos champs de date sont de type Nullable DateTime (i.e. DateTime ?), et que vous obtenez une erreur de compilation du type "Cannot convert lambda expression to type 'System.Collections.Generic.IComparer<...>' because it is not a delegate type", l'interface de l'utilisateur est la suivante réel Le problème est peut-être qu'il n'existe pas de méthode CompareTo pour le type nullable DateTime ? Si vous êtes sûr que l'objet ne sera pas nul, vous pouvez le convertir en DateTime ordinaire pour résoudre le problème.

44 votes

Oui, c'est la "bonne" réponse et elle devrait être beaucoup plus efficace que la création d'un nouveau IEnumerable et sa conversion en une nouvelle liste.

72 votes

Bien sûr, si vous avez besoin d'un tri décroissant, il faut échanger x y y à droite de la flèche => .

238voto

GenericTypeTea Points 27689

Pour faire cela sans LINQ sur .Net2.0 :

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

Si vous êtes sur .Net3.0, alors la solution de LukeH réponse est ce que vous recherchez.

Pour trier sur plusieurs propriétés, vous pouvez toujours le faire dans 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 orderIds.

Cependant, je ne recommanderais pas de s'en tenir aux délégués car cela signifierait beaucoup d'endroits sans réutilisation du code. Vous devriez implémenter un IComparer et de le transmettre à votre Sort méthode. Voir aquí .

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 pour utiliser cette classe IComparer, il suffit de l'instancier et de la passer à votre méthode Sort :

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

1 votes

Excellente réponse, et devrait être la bonne car elle protège la réinitialisation de la liste originale (ce que la version LINQ fera toujours) en fournissant une meilleure encapsulation.

0 votes

@wonton, non. L'idée est de pouvoir avoir différents IComparer implémentations, ce qui nous donne un comportement polymorphe.

0 votes

Ça a marché pour moi. J'ai posté une version de ceci avec des options de tri.

126voto

Prakash Kalakoti Points 1224

La manière la plus simple d'ordonner 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 les suivantes.

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

33voto

Jimmy Hoffa Points 3651

Je le fais 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;
    }
}

Ensuite, il suffit d'appeler .sort() sur votre liste d'ordres.

3 votes

Il faut d'abord tester null en el as la distribution. Ce qui est le but de as comme (ha, ha) (Order)obj lance une exception lorsqu'il échoue. if(orderToCompare == null) return 1; .

0 votes

+1 pour l'utilisation de l'interface, qui rend le code plus facile à maintenir et expose clairement les capacités de l'objet.

0 votes

Si Bill et Ted se montrent un jour, je leur demanderai de me ramener au 16 octobre 2014 pour que je puisse corriger mon erreur ci-dessus as renvoie à null si le casting échoue. Mais au moins le test nul est correct.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X