web-dev-qa-db-fra.com

Comment trier une liste <T> 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 souhaite trier la liste en fonction d'une propriété de l'objet Order. Par exemple, je dois la trier en fonction de la date de la commande ou de l'ID de la commande.

Comment puis-je faire cela en C #?

1071
Shyju

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

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

Si vous devez trier la liste sur place, vous pouvez utiliser la méthode 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 méthode LINQ OrderBy , comme indiqué dans les autres réponses.

644
LukeH

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 LukeH réponse est ce que vous recherchez.

Pour trier sur plusieurs propriétés, vous pouvez toujours le faire au sein 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 des dates croissantes avec décroissantes orderIds.

Cependant, je ne recommanderais pas de coller des délégués car cela signifierait beaucoup d'endroits sans réutilisation du code. Vous devez implémenter un IComparer et le transmettre à 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 puis, pour utiliser cette classe IComparer, il suffit de l’instancier et de la transmettre à votre méthode de tri:

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

Le moyen le plus simple de commander une liste consiste à utiliser OrderByname__

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

Si vous souhaitez commander par plusieurs colonnes comme suit SQL Query.

ORDER BY OrderDate, OrderId

Pour ce faire, vous pouvez utiliser ThenBycomme suit.

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

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

Ensuite, appelez simplement .sort () sur votre liste de commandes

32
Jimmy Hoffa

ne solution classique orientée objet

Tout d'abord, je dois faire une génuflexion à la génialité de LINQ ... Maintenant que nous avons réglé le problème

Une variation sur la réponse de JimmyHoffa. Avec les génériques, le paramètre CompareTo devient de type sûr.

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 capacité de tri par défaut est bien sûr réutilisable. En d'autres termes, chaque client n'a pas à réécrire de manière redondante la logique de tri. Échanger les "1" et "-1" (ou les opérateurs logiques, votre choix) inverse l'ordre de tri.

23
radarbob

// Tri totalement générique à utiliser 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);
    }
18
roger

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 cette version supplémentaire qui accepte un ICompare<U>, afin que vous puissiez personnaliser la comparaison. Cela m'a aidé lorsque j'ai eu besoin de faire une sorte de chaîne 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)));
}
5
Peter

Utiliser LINQ

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

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
4
Daniel A. White
//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
Waqas Ahmed

Une version améliorée de la version de Roger.

Le problème avec GetDynamicSortProperty est que seuls les noms de propriété sont obtenus, mais que se passe-t-il si dans GridView, nous utilisons NavigationProperties? il enverra une exception, car il trouve null.

Exemple:

"Employee.Company.Name;" va planter ... car n'autorise que "Nom" en tant que 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
user1013375

Vous pouvez faire quelque chose de plus générique sur la sélection des propriétés tout en précisant le type de votre choix, dans votre cas, "Commande":

écrivez votre fonction en tant que 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 ensuite l'utiliser comme ça:

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 l'utiliser de la même manière:

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

Ce qui est une manière stupide et inutile de faire un 'OrderBy' dans le style LINQ, mais cela peut vous donner une idée de la façon dont cela peut être implémenté de manière générique.

3
Danny Mor

Laissez-moi compléter la réponse par @LukeH avec un exemple de code, car je l’ai testé, je pense que cela pourrait ê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
molbalga
var obj = db.Items.Where...

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

Aucune des réponses ci-dessus n'était assez générique pour moi alors 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();

Attention cependant aux jeux de données massifs. C'est un code facile, mais qui pourrait vous causer des problèmes si la collection est volumineuse et si le type d'objet de la collection comporte un grand nombre de champs. Le temps d'exécution est NxM où:

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

M = # de propriétés dans l'objet

1
itcropper

Toute personne travaillant avec des types nullables, Value doit utiliser CompareTo.

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

1
Jude

Utiliser LiNQ OrderBy

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

Basé sur GenericTypeTea 's Comparer:
nous pouvons obtenir plus de flexibilité en ajoutant des drapeaux 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 en tant que MyOrderingClass explicitement (plutôt que IComparer)
afin de définir ses propriétés de tri:

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

Du point de vue des performances, le mieux consiste à utiliser une liste triée pour que les données soient triées au fur et à mesure de l'ajout au résultat. D'autres approches nécessitent au moins une itération supplémentaire sur les données et la plupart en créent une copie, ce qui affecte non seulement les performances, mais également l'utilisation de la mémoire. Cela pourrait ne pas être un problème avec quelques centaines d’éléments, mais plutôt avec des milliers, en particulier dans les services où plusieurs demandes simultanées peuvent faire le tri en même temps. Examinez l'espace de noms System.Collections.Generic et choisissez une classe avec tri au lieu de List.

Et évitez les implémentations génériques utilisant la réflexion lorsque cela est possible, cela peut également entraîner des problèmes de performances.

0
user3285954