I have a class called Order
which has properties such as OrderId
, OrderDate
, Quantity
, and Total
. I have a list of this Order
class:
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
I want to sort the list based on one property of the Order
object; for example, either by the order date or the order id.
How can I do this in C#?
The easiest way I can think of is to use Linq:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
If you need to sort the list in-place then you can use the Sort
method, passing a Comparison<T>
delegate:
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
If you prefer to create a new, sorted sequence rather than sort in-place then you can use LINQ's OrderBy
method, as mentioned in the other answers.
x
and y
on the right-hand side of the arrow =>
.
Nullable<>
(take DateTime?
as the example) you can use .Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate))
which will treat null as preceding all non-null values. It is exactly the same as .Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate)
.
To do this without LINQ on .Net2.0:
List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
delegate(Order p1, Order p2)
{
return p1.OrderDate.CompareTo(p2.OrderDate);
}
);
If you're on .Net3.0, then LukeH's answer is what you're after.
To sort on multiple properties, you can still do it within a delegate. For example:
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;
}
);
This would give you ascending dates with descending orderIds.
However, I wouldn't recommend sticking delegates as it will mean lots of places without code re-use. You should implement an IComparer
and just pass that through to your Sort
method. See here.
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;
}
}
And then to use this IComparer class, just instantiate it and pass it to your Sort method:
IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
IComparer
implementations, giving us polymorphic behavior.
Simplest way to order a list is to use OrderBy
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ToList();
If you want to order by multiple columns like following SQL Query.
ORDER BY OrderDate, OrderId
To achieve this you can use ThenBy
like following.
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
Doing it without Linq as you said:
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;
}
}
Then just call .sort() on your list of Orders
null
on the as
cast. Which is the whole point of as
, as (ha, ha) (Order)obj
throws an exception when it fails. if(orderToCompare == null) return 1;
.
as
returns null
if the cast fails. But at least the null-test is right.
List<Order>
so the type should be guaranteed to match on the as
so 2014 you probably didn't write a bug, so much as avoiding an unnecessary guard statement :)
List<Order>
; but you and I both have met the self-encapsulated programmer who's unspoken presumption is "i'm writing this code so it won't be used wrong"
A Classical Object Oriented Solution
First I must genuflect to the awesomeness of LINQ.... Now that we've got that out of the way
A variation on JimmyHoffa answer. With generics the CompareTo
parameter becomes 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();
This default sortability is re-usable of course. That is each client does not have to redundantly re-write the sorting logic. Swapping the "1" and "-1" (or the logic operators, your choice) reverses the sort order.
this
object is greater than null. For the purposes of sorting, a null object reference "is less than" this
object. That's just the way I decided to define how nulls will sort.
// 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);
}
data.OrderBy()
. Bit easier that re-inventing the wheel.
Using LINQ
objListOrder = GetOrderList()
.OrderBy(o => o.OrderDate)
.ToList();
objListOrder = GetOrderList()
.OrderBy(o => o.OrderId)
.ToList();
Here is a generic LINQ extension method that does not create an extra copy of the list:
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)));
}
To use it:
myList.Sort(x=> x.myProperty);
I recently built this additional one which accepts an ICompare<U>
, so that you can customize the comparison. This came in handy when I needed to do a Natural string sort:
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)));
}
OrderBy
does all of that internally.
void
extension method in this spirit: static class Extensions { public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector) { self.SortBy(keySelector, Comparer<TKey>.Default); } public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) { self.Sort((x, y) => comparer.Compare(keySelector(x), keySelector(y))); } }
Can be supplemented by a SortByDescending
method. @Servy's comments apply to my code as well!
//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;
Please let me complete the answer by @LukeH with some sample code, as I have tested it I believe it may be useful for some:
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 ...
}
Anybody working with nullable types, Value
is required to use CompareTo
.
objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
An improved of Roger's version.
The problem with GetDynamicSortProperty is that only get the property names but what happen if in the GridView we use NavigationProperties? it will send an exception, since it finds null.
Example:
"Employee.Company.Name; " will crash... since allows only "Name" as a parameter to get its value.
Here's an improved version that allows us to sort by Navigation Properties.
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;
}
}
You can do something more generic about the properties selection yet be specific about the type you're selecting from, in your case 'Order':
write your function as a generic one:
public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
{
return (from order in orders
orderby propertySelector(order)
select order).ToList();
}
and then use it like this:
var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);
You can be even more generic and define an open type for what you want to order:
public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
{
return (from item in collection
orderby propertySelector(item)
select item).ToList();
}
and use it the same way:
var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);
Which is a stupid unnecessary complex way of doing a LINQ style 'OrderBy', But it may give you a clue of how it can be implemented in a generic way
var obj = db.Items.Where...
var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
Suppose you have the following code, in this code, we have a Passenger class with a couple of properties that we want to sort based on.
public class Passenger
{
public string Name { get; }
public string LastName { get; }
public string PassportNo { get; }
public string Nationality { get; }
public Passenger(string name, string lastName, string passportNo, string nationality)
{
this.Name = name;
this.LastName = lastName;
this.PassportNo = passportNo;
this.Nationality = nationality;
}
public static int CompareByName(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.Name, passenger2.Name);
}
public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.LastName, passenger2.LastName);
}
public static int CompareNationality(Passenger passenger1, Passenger passenger2)
{
return String.Compare(passenger1.Nationality, passenger2.Nationality);
}
}
public class TestPassengerSort
{
Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");
public void SortThem()
{
Passenger[] passengers = new Passenger[] { p1, p2, p3 };
List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };
Array.Sort(passengers, Passenger.CompareByName);
Array.Sort(passengers, Passenger.CompareByLastName);
Array.Sort(passengers, Passenger.CompareNationality);
passengerList.Sort(Passenger.CompareByName);
passengerList.Sort(Passenger.CompareByLastName);
passengerList.Sort(Passenger.CompareNationality);
}
}
So you can implement your sort structure by using Composition delegate.
Make use of LiNQ OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
Based on GenericTypeTea's Comparer : we can obtain more flexibility by adding sorting flags :
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; }
}
In this scenario, you must instantiate it as MyOrderingClass explicitly( rather then IComparer ) in order to set its sorting properties :
MyOrderingClass comparer = new MyOrderingClass();
comparer.DateDescending = ...;
comparer.OrderIdDescending = ...;
orderList.Sort(comparer);
None of the above answers were generic enough for me so I made this one:
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();
Careful on massive data sets though. It's easy code but could get you in trouble if the collection is huge and the object type of the collection has a large number of fields. Run time is NxM where:
N = # of Elements in collection
M = # of Properties within Object
If you need to sort the Id that is string in Question entity
Use Sort function and delegate to sort the Id after parsing the Id value
class Question
{
public List<QuestionInfo> Questions Info{ get; set; }
}
class QuestionInfo
{
public string Id{ get; set; }
public string Questions{ get; set; }
}
var questionnaire = new Question();
questionnaire.QuestionInfo.Sort((x, y) => int.Parse(x.Id, CultureInfo.CurrentCulture) - int.Parse(y.Id, CultureInfo.CurrentCulture));
I made this extension method for List<T>
.
The extension method takes the property you wish to sort as a parsed string and then uses the OrderBy
method of the List<T>
. Then it sets each index of the original list to the same index of the ordered list.
public static class ListExtensions {
public static void SortBy<T>(this List<T> list, string property, bool reverse = false) {
List<T> ordered = list.OrderBy(obj => obj.GetType().GetProperty(property).GetValue(obj, null)).ToList();
for (int i = 0; i < list.Count; i++)
list[i] = reverse ? ordered[list.Count - 1 - i] : ordered[i];
}
}
If an object in the list has the property Name
you sort your list testList
as so:
//For normal sorting order
testList.SortBy("Name");
//For reverse sorting order
testList.SortBy("Name", true);
I would recommend that you change the name of SortBy
, to something like Prefix_SortBy
. To prevent potential collisions if you import another library.
I know this method works for alphabetical and numerical ordering. Its sorting capabilites may be limited yet it is very simple to operate.
If there are some major flaws or issues with this, do tell, I've been programming C# for about 3 months.
Best regards
From performance point of view the best is to use a sorted list so that data is sorted as it is added to result. Other approaches need at least one extra iteration on data and most create a copy of data so not only performance but memory usage will be affected too. Might not be an issue with couple of hundreds of elements but will be with thousands, especially in services where many concurrent requests may do sorting at the same time. Have a look at System.Collections.Generic namespace and choose a class with sorting instead of List.
And avoid generic implementations using reflection when possible, this can cause performance issues too.
hi just to come back at the question. If you want to sort the List of this sequence "1" "10" "100" "200" "2" "20" "3" "30" "300" and get the sorted items in this form 1;2;3;10;20;30;100;200;300 you can use this:
public class OrderingAscending : IComparer<String>
{
public int Compare(String x, String y)
{
Int32.TryParse(x, out var xtmp);
Int32.TryParse(y, out var ytmp);
int comparedItem = xtmp.CompareTo(ytmp);
return comparedItem;
}
}
and you can use it in code behind in this form:
IComparer<String> comparerHandle = new OrderingAscending();
yourList.Sort(comparerHandle);
Success story sharing
listWithObjects = listWithObjects.OrderByDescending(o => o.Status).ToList();
suffice for such an endeavor?