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#?
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();
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.
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);
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();
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
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.
// 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);
}
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)));
}
utilisant LINQ
objListOrder = GetOrderList()
.OrderBy(o => o.OrderDate)
.ToList();
objListOrder = GetOrderList()
.OrderBy(o => o.OrderId)
.ToList();
//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;
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;
}
}
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
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 ...
}
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
var obj = db.Items.Where...
var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
faites usage de LiNQ OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
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);
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.
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));