Start learning free

LESSON 266 OF 674 · TUPLES, SORTING & BUILT-INS

Custom Sorting in C#: OrderBy and ThenBy

Lesson Read the full explanation

TL ; DR:

LINQ sorts by your own rule. OrderBy takes a lambda that says what to sort each item by.


List<int> nums = new List<int>(new[] {25, 13, 5, 41, 33});

List<int> byLastDigit = nums.OrderBy(n => n % 10).ThenBy(n => n).ToList();
// 41 13 33 5 25

List<int> scores = new List<int>(new[] {70, 95, 82});

List<int> highFirst = scores.OrderByDescending(s => s).ToList();
// 95 82 70


Full lesson:

You met Sort() in the Shopping Cart project. It sorts a list from small to large, or from A to Z. Sometimes you need another order. The shortest word may have to come first, or the highest score.

LINQ has methods for that. You met LINQ and its lambdas with Where and Select. Here the lambda says what to sort each item by. The lambda w => w.Length sorts words by their length.

  • The method OrderBy(w => w.Length) sorts from small to large.
  • The method OrderByDescending(s => s) sorts from large to small.
  • The method ThenBy(n => n) adds a second rule. It only sorts items that tie on the first rule.
  • The method ThenByDescending is a second rule from large to small.

Above, OrderBy(n => n % 10) sorts the numbers by their last digit. Then ThenBy(n => n) puts numbers with the same last digit from small to large.

Don't call OrderBy twice. The second OrderBy sorts everything again and forgets the first rule. Use ThenBy for the second rule.

LINQ makes a new sequence and leaves the list as it was. End with ToList() to get a List back. The list's own Sort() changes the list itself instead.


Your task

A pet shop sells toys. Their prices are in prices. Make a List named highFirst with the prices from high to low.

The shop's pets are in names. Make a List named byLastLetter with the names sorted by their last letter, in alphabetical order. Names with the same last letter go from A to Z.

The code already prints both, one item per line.

Expected output:

30
12
8
5
Bella
Luna
Bo
Coco
Max
Rex
STEP 1 OF 2
    Ctrl + Enter to check

    C# · TUPLES, SORTING & BUILT-INS

    Custom Sorting in C#, explained

    Why custom sorting

    A list's Sort() puts numbers from small to large and strings from A to Z. Often you need another order: the highest price first, the shortest word first, or names ordered by their last letter. LINQ has methods for that. Each one takes a lambda that says what to sort each item by. In a file of your own, add using System.Linq;.

    The sorting methods

    • OrderBy(w => w.Length) sorts by what the lambda returns, from small to large.
    • OrderByDescending(s => s) sorts from large to small.
    • ThenBy(n => n) adds a second rule. It only orders items that tie on the first rule.
    • ThenByDescending is a second rule from large to small.
    List<int> scores = new List<int>(new[] {70, 95, 82});
    List<int> highFirst = scores.OrderByDescending(s => s).ToList();
    
    List<int> nums = new List<int>(new[] {25, 13, 5, 41, 33});
    List<int> byLastDigit = nums.OrderBy(n => n % 10).ThenBy(n => n).ToList();

    Printed with string.Join(" ", ...), byLastDigit reads 41 13 33 5 25. The first rule sorts by last digit; the second puts numbers with the same last digit (13 and 33, or 5 and 25) from small to large. highFirst reads 95 82 70.

    To send ties from large to small instead, use ThenByDescending: nums.OrderBy(n => n % 10).ThenByDescending(n => n) reads 41 33 13 25 5, so 33 now comes before 13.

    Things to know

    • Don't call OrderBy twice. The second OrderBy sorts everything again and forgets the first rule. On the numbers above, nums.OrderBy(n => n % 10).OrderBy(n => n) gives 5 13 25 33 41. Use ThenBy for the second rule.
    • LINQ makes a new sequence and leaves the list as it was. End with ToList() to get a List back. With words holding pear fig apple, words.OrderBy(w => w.Length).ToList() reads fig pear apple and words still reads pear fig apple.
    • The list's own Sort() changes the list itself instead.

    The lesson's task

    A pet shop has toy prices 12, 5, 30 and 8, and pets Rex, Luna, Bella, Max, Bo and Coco. Make highFirst with the prices from high to low, and byLastLetter with the names sorted by their last letter, with names that share a last letter going from A to Z.

    1. Call OrderByDescending on prices with p => p: each price is sorted by itself, from the biggest down. Then ToList().
    2. For the names, OrderBy with the last letter, n[n.Length - 1]. A string indexed with a number gives a char, and chars sort in alphabetical order.
    3. Add ThenBy(n => n), so names with the same last letter go from A to Z. Max and Rex both end in x, and this puts Max first. Then ToList().
    class Solution {
        public void MainFunction() {
            List<int> prices = new List<int>(new[] {12, 5, 30, 8});
            List<string> names = new List<string>(new[] {"Rex", "Luna", "Bella", "Max", "Bo", "Coco"});
    
            List<int> highFirst = prices.OrderByDescending(p => p).ToList();
            foreach (int price in highFirst) {
                Console.WriteLine(price);
            }
    
            List<string> byLastLetter = names.OrderBy(n => n[n.Length - 1]).ThenBy(n => n).ToList();
            foreach (string name in byLastLetter) {
                Console.WriteLine(name);
            }
        }
    }

    The prices print as 30, 12, 8, 5. The names end in a (Bella, Luna), o (Bo, Coco) and x (Max, Rex), so they print as:

    Bella
    Luna
    Bo
    Coco
    Max
    Rex

    Common mistakes

    • Two OrderBy calls. Only the last one counts; chain ThenBy instead.
    • Forgetting ToList(). OrderBy returns a sequence, which can't be assigned to a List<int> variable.
    • Mixing up the direction. OrderBy goes small to large; OrderByDescending goes large to small.
    • Expecting the original list to change. Print the new list, not prices or names.

    Key takeaways

    • OrderBy and OrderByDescending take a lambda that picks what to sort by.
    • ThenBy and ThenByDescending add rules that only matter on ties.
    • LINQ returns a new sequence; ToList() turns it back into a List.
    Keep going with C#500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free