BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

LINQ
.NET 3.5+

LINQ Results Ordering

The fifth part of the LINQ to Objects tutorial looks at sorting collections or the results of LINQ queries. This is achieved using the group of five standard query operators that relate to ordering, or the equivalent clauses in query expression syntax.

Sorting Results

An essential part of working with queries is ordering the results. Sorting is necessary when displaying items to the user or when sending information between systems where the receiving software expects the data to be provided in a specific order. LINQ provides several methods for sorting data using either standard query operators or query expression syntax. Information can be sorted using one or more properties or fields from the source collection's objects, or by the results of expressions that use that data. You can rearrange collections and sort results in either ascending or descending order, or a mixture of the two when using multiple sort expressions.

Ordering Standard Query Operators

LINQ has five standard query operators that are used to order collections. Four of these can be used to sort information into any order that you require, using one or more sort expressions. These are the OrderBy, OrderByDescending, ThenBy and ThenByDescending methods, which are all extension methods of the IEnumerable<T> interface. The fifth method simple reverses the items in a collection and is named, "Reverse".

The ordering operators can be combined with other methods, such as those that control filtering and projection. In this article we will use only the ordering operators to demonstrate their use using simple examples.

Before we start, we need an example class to work with. In this article we will use a class that represents a stock item in a stock system or eCommerce web site. Each stock item includes a name, a category and a price. To simplify the examples, all three items can be passed to the constructor to instantiate a fully populated object. The ToString method is overridden to return a combination of the three properties.

public class StockItem
{
    public string Name { get; set; }
    public string Category { get; set; }
    public double Price { get; set; }

    public StockItem(string name, string category, double price)
    {
        Name = name;
        Category = category;
        Price = price;
    }

    public override string ToString()
    {
        return string.Format("{0}/{1}/{2}", Name, Category, Price);
    }
}

We will use the same collection of stock items for all of the sorting samples:

var stock = new List<StockItem>
{
    new StockItem("Apple", "Fruit", 0.30),
    new StockItem("Banana", "Fruit", 0.35),
    new StockItem("Orange", "Fruit", 0.29),
    new StockItem("Cabbage", "Vegetable", 0.49),
    new StockItem("Carrot", "Vegetable", 0.29),
    new StockItem("Lettuce", "Vegetable", 0.30),
    new StockItem("Milk", "Dairy", 1.12)
};

Using OrderBy

The basic sorting operator is OrderBy. This sorts a collection into ascending order. A stable sort algorithm is used, meaning that where two items match and cannot be sorted, their original order is preserved. OrderBy applies a primary order to a collection. If you use two OrderBy calls in the same query, the second will override the first.

The basic version of the method accepts a single argument. As with many standard query operators, you must provide a Func delegate, usually a lambda expression, that returns the item to be sorted. The ordering is performed using the default comparer for the data type returned by the delegate. In the sample below, the lambda expression returns the name of the stock item. The results are therefore in ascending alphabetical order of name.

var results = stock.OrderBy(s => s.Name);

/* RESULTS

Apple/Fruit/0.3
Banana/Fruit/0.35
Cabbage/Vegetable/0.49
Carrot/Vegetable/0.29
Lettuce/Vegetable/0.3
Milk/Dairy/1.12
Orange/Fruit/0.29

*/

Using Alternative Comparers

In some cases the default comparer does not give the results that you require. In those cases you can supply an alternative comparer, which could be one of the standard classes provided by the .NET framework or a custom comparer. To demonstrate this, we can create a new comparer that implements the generic IComparer<T> interface. The following class compares two stock items based upon the length of the text returned by the ToString method. Shorter strings are deemed to be "less than" longer strings. The comparer is unlikely to be used in a real-world scenario but is useful for the sample code that follows.

public class ToStringLengthComparer : IComparer<StockItem>
{
    public int Compare(StockItem x, StockItem y)
    {
        return x.ToString().Length - y.ToString().Length;
    }
}

To use the comparer, we simply provide an instance of it to the second parameter of the OrderBy method as follows. Note the results are in length order.

var results = stock.OrderBy(s => s, new ToStringLengthComparer());

/* RESULTS

Apple/Fruit/0.3
Milk/Dairy/1.12
Banana/Fruit/0.35
Orange/Fruit/0.29
Carrot/Vegetable/0.29
Lettuce/Vegetable/0.3
Cabbage/Vegetable/0.49

*/
19 July 2010