TOC
LINQ:

Sorting data: the OrderBy() & ThenBy() methods

So, now that we have learned through the previous article how to get data from the data source with LINQ and filter it with the Where() method, the next step could be to sort the data. We have used lists of objects, either numeric or based on e.g. a User class, so the order we got the items in was the same order that they were added to the list. However, as we have previously talked about, your data source for LINQ operations might as well be an XML document or a database. Therefore, the ability to properly sort the data, once we have the data we need, is crucial. Fortunately for us, LINQ has several easy-to-use methods for sorting data - let's try a basic example first:

List<int> numbers = new List<int>()
{
    1, 7, 2, 61, 14
};
List<int> sortedNumbers = numbers.OrderBy(number => number).ToList();
foreach (int number in sortedNumbers)
    Console.WriteLine(number);

That was easy, right? Just call the OrderBy() method and supply the object or the member of the object to sort by, and a sorted list will be returned. And of course you can do it just as easy with strings, as we'll see in the next example, but let's get the items in descending (from largest to smallest/from Z to A) order:

List<string> cityNames = new List<string>()
{
    "Amsterdam", "Berlin", "London", "New York"
};
List<string> sortedCityNames = cityNames.OrderByDescending(city => city).ToList();
foreach (string cityName in sortedCityNames)
    Console.WriteLine(cityName);

We do the exact same thing as before, except that we use the OrderByDescending() method instead of the OrderBy() method. But of course you can get your list of integers and strings sorted easily - that's a piece of cake! However, thanks to LINQ, it's pretty much just as easy to sort more complex objects. Here's an example:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqOrder2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<User> listOfUsers = new List<User>()
            {
                new User() { Name = "John Doe", Mail = "john@doe.com", Age = 42 },
                new User() { Name = "Jane Doe", Mail = "jane@doe.com", Age = 34 },
                new User() { Name = "Joe Doe", Mail = "joe@doe.com", Age = 8 },
                new User() { Name = "Another Doe", Mail = "another@doe.com", Age = 15 },
            };

            List<User> usersByAge = listOfUsers.OrderBy(user => user.Age).ToList();
            foreach (User user in usersByAge)
                Console.WriteLine(user.Name + ": " + user.Age + " years");
        }

        class User
        {
            public string Name { get; set; }
            public string Mail { get; set; }
            public int Age { get; set; }
        }
    }
}

This is of course a more complete example, with the User class included and the list of users initialized, but as you can see, the actual sorting is still very short and precise: When calling the OrderBy() method, we still just supply a parameter name and then use this parameter to access the Age property of the User objects. The result is a perfectly age-sorted list! But what if we want to order by more than one property?

ThenBy() and ThenByDescending()

In the example above, we sorted the list of users by their age, but what if there were several users of the same age? A pretty common scenario, even in our tiny example - just imagine that Jane and John are the same age and their kids are twins. In that case, it would be practical to control the order even after the data source had been arranged by age. For that, we can use the ThenBy() and ThenByDescending() methods. They do just what the name indicates: Control the order after the initial sort. We can use that to get the list of users sorted, first by age and then alphabetically by name:

List<User> listOfUsers = new List<User>()
{
    new User() { Name = "John Doe", Mail = "john@doe.com", Age = 42 },
    new User() { Name = "Jane Doe", Mail = "jane@doe.com", Age = 42 },
    new User() { Name = "Joe Doe", Mail = "joe@doe.com", Age = 8 },
    new User() { Name = "Jenna Doe", Mail = "another@doe.com", Age = 8 },              
};

List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenBy(user => user.Name).ToList();
foreach (User user in sortedUsers)
    Console.WriteLine(user.Name + ": " + user.Age + " years");

Quite simple but very effective! You can even chain multiple ThenBy() method calls, in case your data is more complex than the data from our test case. And of course, you can mix and match the OrderBy(), OrderByDescending(), ThenBy() and ThenByDescending() methods in whatever way you need:

List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenByDescending(user => user.Name).ToList();
foreach (User user in sortedUsers)  
    Console.WriteLine(user.Name + ": " + user.Age + " years");

We're mostly using the method-based syntax of LINQ in this tutorial, but as always, I will take one of the examples in the article and show you how it would look with the query syntax - here's the latest example, including a LINQ query syntax version:

// Method syntax
List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenByDescending(user => user.Name).ToList();

// Query syntax
List<User> sortedUsersQ = (from user in listOfUsers orderby user.Age ascending, user.Name descending select user).ToList();

As you can see, the syntax is slightly different - the direction (ascending or descending) is specified directly after the field to order by (ascending is actually implicit, but I included it to show you the difference). Also, there's no "ThenBy" - instead, you just separate multiple sort instructions with a comma. Of course, in the end, both queries will give you the same result.

Summary

Using the OrderBy() and ThenBy() methods (as well as their "descending" counterparts), you can easily get your data sorted just the way you want it. And remember, just like any other LINQ method, the actual data source is not manipulated - instead, you get a sorted copy of the original data source, which you can work with.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!