Start learning free

LESSON 198 OF 347 · SORTING

Sorting in C#: Array.Sort, Comparisons and Lambdas

Lesson Read the full explanation

In order to sort a static array we can use Array.Sort() function which sorts the elements of a given array in ascending order (default), descending order or user-defined order.

Similarly, if we want to sort an ArrayList, we can use Collections.sort() which works the same way.

Important: Sorting in reverse or with a specified order using a comparator is possible only if we use objects and not primitives. This means that it will work for int[] array, but not on int[], at least not as easy as we think.

Syntax:

Let's see some examples below. Sorting an array of integers in ascending order

int[] numbers = {11, 3, 7, 5, 2};

Array.Sort(numbers); // numbers becomes [2, 3, 5, 7, 11]

Sorting an ArrayList of Integers in ascending order

List<int> arr = new List<>(List.of(11, 3, 7, 5, 2));

Collections.sort(arr, Collections.reverseOrder()); // numbers becomes [2, 3, 5, 7, 11]

Sorting an array of integers in descending order

int[] numbers = {11, 3, 7, 5, 2};

Array.Sort(numbers, Collections.reverseOrder()); // compile error!

Notice that we have an int[] array, so we won't be able to sort it in reverse directly! We need to transform it into Integer[] array as below:

int[] numbers = {11, 3, 7, 5, 2};

// transform it to int[]
int[] arr = Arrays.stream(numbers).boxed().toArray(int[]::new);

// Now we can sort it
Array.Sort(arr, Collections.reverseOrder()); //arr becomes [11, 7, 5, 3, 2]

Sorting an array of Strings in descending order

string[] names = {"Mircea", "Andy", "Xavier", "Michael", "Christina"};

Array.Sort(names, Collections.reverseOrder()); // --> ['Xavier', 'Mircea', 'Michael', 'Christina', 'Andy']

Sort with custom function (comparator):

Array.Sort() function supports a basic comparison function that we can use to specify how we want to sort:

int compare(int a, int b) {
    return a - b;
}

This can be written as a lambda function (nameless function we can easily pass to sort()) as follows:

(a, b) -> {
    return a - b;
}

If the function just returns one thing, we can write it as follows:

(a, b) -> a - b

When the sort() function compares two values, it sends the values to the compare function, and sorts the values according to the returned (negative, zero, positive) value:

  • If the result is negative a is sorted before b.
  • If the result is positive b is sorted before a.
  • If the result is 0 no changes are done with the sort order of the two values.

All we need to is using the compare function (as a lambda function) inside the sort() method:

int[] numbers = {3, 23, 12};

// using the short version of the lambda function
Array.Sort(numbers, (a, b) -> a - b); // --> 3, 12, 23

To demonstrate a basic example, let’s assume the requirement for ordering a specific list is the length of the strings in the list, shortest to longest. We will be using the length property of each string to compare them.

string words = {"banana", "pie", "Washington", "book"};

Array.Sort(words, (a, b) -> a.length - b.length); // --> ['pie', 'book', 'banana', 'Washington']

Example: sorting array of pairs

Let's say we want to sort an array, where each element is another array consisting of two numbers.

This time we will be using int[][] since an array int[] is treated as an object so it can be sorted with a compare function

int[][] pairs = {{1, 5}, {12, 5}, {10, 1}, {1, 3}};

Array.Sort(pairs, (a, b) -> {
    // in case the first values of each pair are equal,
    // compare them using their second values
    if (a[0] == b[0]) {
        return a[1] - b[1];
    }
    return a[0] - b[0];
});
// pairs --> [[1, 3], [1, 5], [10, 1], [12, 5]]

Time Complexity:

Sorting an array via Array.Sort() takes O(n * log n * C) time, where C is the time taken to compare two elements from the array.

It takes O(1) (constant time) to compare two numbers, therefore sorting an array of numbers takes O(n log n).

However, it takes O(L) to compare two strings, therefore sorting an array of strings takes O(n * log n * L), where L is the average length of the strings.


Space Complexity:

The sort() method sorts the elements of an array in place (transforms the array directly, using no auxiliary data structure), so the extra space used for this is O(1).


Assignment
Follow the Coding Tutorial and let's sort some arrays.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 6
    Ctrl + Enter to check

    C# · SORTING

    Sorting in C#, explained

    Sorting arrays in C#

    C# can sort most data with one call. Array.Sort rearranges an array in place, in ascending order by default:

    int[] numbers = {11, 3, 7, 5, 2};
    Array.Sort(numbers);   // numbers is now {2, 3, 5, 7, 11}
    

    A List<T> has its own method with the same behaviour:

    List<int> list = new List<int> {11, 3, 7, 5, 2};
    list.Sort();           // list is now 2, 3, 5, 7, 11
    

    Both methods sort in place and return nothing. If you need the original order afterwards, copy the data first.

    Custom orders with a comparison

    Default ascending order is not always what you want. Both Array.Sort and List<T>.Sort accept a comparison function that decides the order of any two elements. It takes two values, a and b, and returns an int:

    • negative: a goes before b;
    • positive: b goes before a;
    • zero: the two are considered equal for this sort.

    The comparison is usually written as a lambda, a nameless function. The full form has a block body, and an expression body is the short form when the function is a single return:

    (a, b) => { return a - b; }
    (a, b) => a - b
    

    To sort integers in descending order, flip the subtraction:

    int[] numbers = {3, 23, 12};
    Array.Sort(numbers, (a, b) => b - a);   // 23, 12, 3
    

    Unlike in some other languages, C# needs no wrapper type: a plain int[] can be sorted with a comparison directly.

    Sorting by a property

    The comparison can look at anything about the elements. To order words from shortest to longest:

    string[] words = {"banana", "pie", "Washington", "book"};
    Array.Sort(words, (a, b) => a.Length - b.Length);
    // pie, book, banana, Washington
    

    Sorting arrays of pairs

    For an array of arrays, such as int[][], each element is an int[] and the comparison picks which values to compare. To order pairs by their first value, then by their second value when the first values tie:

    int[][] pairs = new int[][] {
        new int[] {1, 5},
        new int[] {12, 5},
        new int[] {10, 1},
        new int[] {1, 3}
    };
    
    Array.Sort(pairs, (a, b) => {
        if (a[0] == b[0]) {
            return a[1] - b[1];
        }
        return a[0] - b[0];
    });
    // {1, 3}, {1, 5}, {10, 1}, {12, 5}
    

    Sorting strings

    For text, compare with CompareTo, which returns a negative number, zero or a positive number like the comparison above. Lowercasing both sides makes the order ignore case:

    string[] names = {"Mircea", "john", "Andy"};
    Array.Sort(names, (a, b) => a.ToLower().CompareTo(b.ToLower()));
    // Andy, john, Mircea
    

    A built-in alternative does the same job: Array.Sort(names, StringComparer.OrdinalIgnoreCase). Without any case handling, an ordinal comparison puts every uppercase letter before every lowercase one, so "john" would come last.

    Gotchas

    • Subtraction can overflow. a - b works for small numbers, but int.MinValue - 1 wraps around silently to a large positive number and the order comes out wrong. a.CompareTo(b) is always safe, and b.CompareTo(a) gives descending order.
    • Array.Sort is not stable. Elements that compare as equal may change their relative order. If that matters, LINQ's OrderBy is stable, though it returns a new sequence instead of sorting in place.
    • The sort changes the array. Code that still needs the old order must keep a copy.
    • The comparison must be consistent. If it says a comes before b, it should say b comes after a. Inconsistent rules give unpredictable results.
    • Method names are capitalised. It's Array.Sort and list.Sort, and Length for arrays, not length.

    Complexity

    Sorting n elements takes O(n log n * C) time, where C is the cost of one comparison. Comparing two numbers is constant time, so sorting numbers is O(n log n). Comparing two strings takes time proportional to their length, so sorting n strings of average length L costs O(n log n * L). The sort happens in the array itself and doesn't allocate a second array.

    The exercise, step by step

    1. Declare numbers. An int[] holding {2, 1, 3, 10}.
    2. Sort it descending. Call Array.Sort with the lambda (a, b) => b - a. A positive result puts b first, so larger numbers come first. Without the lambda, the sort would be ascending.
    3. Declare coordinates. A jagged int[][] created with new int[][], holding the pairs {3, 2}, {2, 3} and {3, 5}, one per line.
    4. Sort the pairs. The first value ascending; when the first values are equal, the second value descending. In the block-bodied lambda, return b[1] - a[1] when a[0] == b[0] (reversed for descending), and a[0] - b[0] otherwise.
    5. Declare names. A string[] with "Mircea", "john" and "Andy".
    6. Sort the names ignoring case. A lambda that compares a.ToLower() with b.ToLower() using CompareTo.
    int[] numbers = {2, 1, 3, 10};
    Array.Sort(numbers, (a, b) => b - a);
    
    int[][] coordinates = new int[][] {
        new int[] {3, 2},
        new int[] {2, 3},
        new int[] {3, 5}
    };
    Array.Sort(coordinates, (a, b) => {
            if (a[0] == b[0]) {
                return b[1] - a[1];
            }
            return a[0] - b[0];
    });
    
    string[] names = {"Mircea", "john", "Andy"};
    Array.Sort(names, (a, b) => a.ToLower().CompareTo(b.ToLower()));
    

    Afterwards, numbers is {10, 3, 2, 1}, coordinates is {2, 3}, {3, 5}, {3, 2}, and names is {"Andy", "john", "Mircea"}.

    Key takeaways

    • Array.Sort(array) and list.Sort() sort ascending, in place.
    • A comparison lambda (a, b) => ... returns negative, zero or positive to define any order.
    • Flip the operands for descending order; compare a second field to break ties.
    • Prefer CompareTo to subtraction when values may be extreme.
    • Sorting numbers is O(n log n), and Array.Sort is not stable.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free