1411
votes

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

Now I want to sort the list based on one property of the Order object, for example I need to sort it by the order date or order id.

How can i do this in C#?

22

22 Answers

2017
votes

The easiest way I can think of is to use Linq:

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

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.

231
votes

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);
122
votes

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

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

29
votes

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.

18
votes

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

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

Using LINQ

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

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
4
votes
//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;
4
votes

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 ...
}
3
votes

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


    } 
3
votes

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

3
votes
var obj = db.Items.Where...

var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
1
votes

Make use of LiNQ OrderBy

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

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);  
1
votes

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

1
votes

Anybody working with nullable types, Value is required to use CompareTo.

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

1
votes

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.

0
votes

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.

0
votes

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);
0
votes
  • 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));