C# sort List
last modified November 6, 2026
In this article we show how to sort list elements in C# language.
Sorting
In computer science, sorting is arranging elements in an ordered sequence. Over the years, several algorithms were developed to perform sorting on data, including merge sort, quick sort, selection sort, or bubble sort. (The other meaning of sorting is categorizing; it is grouping elements with similar properties.)
The opposite of sorting, rearranging a sequence of elements in a random or meaningless order, is called shuffling.
Data can be sorted alphabetically or numerically. The sort key specifies the criteria used to perform the sort. It is possible to sort objects by multiple keys. For instance, when sorting users, the names of the users could be used as the primary sort key, and their occupation as the secondary sort key.
Sort method or with LINQ's OrderBy.
Sorting order
A standard order is called the ascending order: a to z, 0 to 9. The reverse order is called the descending order: z to a, 9 to 0. For dates and times, ascending means that earlier values precede later ones, e.g. 1/1/2020 sorts ahead of 1/1/2021.
Stable sort
A stable sort is one where the initial order of equal elements is preserved. Some sorting algorithms are naturally stable, some are unstable. For instance, the merge sort and the bubble sort are stable sorting algorithms. On the other hand, heap sort and quick sort are examples of unstable sorting algorithms.
Consider the following values: 3715593. A stable
sorting produces the following: 1335579. The
ordering of the values 3 and 5 is kept. An unstable sorting may produce the
following: 1335579.
The List.Sort and Array.Sort methods use introsort,
which is an unstable algorithm. The LINQ OrderBy and
ThenBy methods perform a stable sort. We demonstrate
the difference later in the article.
Sorting in C#
In C#, we can sort with the built-in Sort and
OrderBy methods. The order of elements is determined by the
Comparison delegate, the IComparer interface, or
the IComparable interface implemented by the elements.
C# List Sort method
The Sort method sorts the elements or a portion of the elements
in the list.
The method has four overloads:
Sort()- sorts the elements in the entireList<T>using the default comparer.Sort(Comparison<T>)- sorts the elements in the entireList<T>using the specifiedComparison<T>.Sort(IComparer<T>)- sorts the elements in the entireList<T>using the specified comparer.Sort(Int32, Int32, IComparer<T>)- sorts the elements in a range of elements inList<T>using the specified comparer.
Sort method sorts the list in-place,
while LINQ's OrderBy method returns a new sorted sequence and
leaves the list unchanged.
The comparison method
The sorting algorithms are already built into the standard library. If the data does not have a natural order, or we want a different one, we need to provide a comparison method (either a class method or a lambda expression) which tells the underlying sorting algorithm how to sort the data: which attributes to compare and in what way.
public int CompareTo(Card? other)
{
if (other is null) return 1;
var index = Rank.CompareTo(other.Rank);
if (index == 0) index = Suit.CompareTo(other.Suit);
return index;
}
For instance, this comparison method sorts the objects by the
Rank and if the rank is the same, then by Suit. We
always compare two elements; in our case, two card objects. The comparison
method returns 0 if the elements are equal, a negative number when the first
element is less than the second, and a positive number when the first element
is greater than the second. The callers must not rely on the values being
exactly -1 or 1.
Oftentimes, our comparison function calls other comparison functions; in our
case, the Rank and the Suit are enumerations and we
compare them with the built-in CompareTo method.
C# List sort integers
The following example sorts integers.
List<int> nums = [2, 1, 8, 0, 4, 3, 5, 7, 9];
nums.Sort();
Console.WriteLine(string.Join(",", nums));
nums.Reverse();
Console.WriteLine(string.Join(",", nums));
nums.Sort((a, b) => b.CompareTo(a));
Console.WriteLine(string.Join(",", nums));
C# uses the default comparer to sort integers numerically. The
Sort method orders the integers in ascending order.
nums.Reverse();
The Reverse method reverses the order of the elements. Called on
a sorted list, it gives us the descending order.
nums.Sort((a, b) => b.CompareTo(a));
Alternatively, we sort in descending order directly by swapping the arguments in the comparison.
$ dotnet run 0,1,2,3,4,5,7,8,9 9,8,7,5,4,3,2,1,0 9,8,7,5,4,3,2,1,0
C# sort integers with LINQ
The following example sorts integers with LINQ. In LINQ, we can choose between the query syntax and the method syntax.
List<int> nums = [2, 1, 8, 0, 4, 3, 5, 7, 9];
var res1 = from num in nums
orderby num
select num;
Console.WriteLine(string.Join(" ", res1));
var res2 = from num in nums
orderby num descending
select num;
Console.WriteLine(string.Join(" ", res2));
Console.WriteLine(string.Join(" ", nums.Order()));
Console.WriteLine(string.Join(" ", nums.OrderDescending()));
Console.WriteLine(string.Join(" ", nums));
The example sorts integers in ascending and descending order.
var res2 = from num in nums
orderby num descending
select num;
In the query syntax, the orderby clause sorts the elements; the
descending keyword reverses the order.
Console.WriteLine(string.Join(" ", nums.Order()));
Console.WriteLine(string.Join(" ", nums.OrderDescending()));
The Order and OrderDescending methods, available
since .NET 7, sort the elements by themselves without a key selector.
Console.WriteLine(string.Join(" ", nums));
The original list is not modified.
$ dotnet run 0 1 2 3 4 5 7 8 9 9 8 7 5 4 3 2 1 0 0 1 2 3 4 5 7 8 9 9 8 7 5 4 3 2 1 0 2 1 8 0 4 3 5 7 9
C# List sort strings
Like integers, strings are sorted by Sort alphabetically by
default without any additional effort.
List<string> words = ["falcon", "order", "war",
"sky", "ocean", "blue", "cloud", "boy"];
words.Sort();
Console.WriteLine(string.Join(",", words));
words.Reverse();
Console.WriteLine(string.Join(",", words));
The example sorts strings in ascending and descending order. The default string comparer is culture-sensitive; it uses the rules of the current culture.
$ dotnet run blue,boy,cloud,falcon,ocean,order,sky,war war,sky,order,ocean,falcon,cloud,boy,blue
C# List sort accented strings
In order to sort accented strings according to the rules of a language, we provide a comparer for the appropriate culture.
using System.Globalization;
List<string> words = [ "čaj", "auto", "drevo", "cibuľa",
"čučoriedka", "banán", "čerešňa",
"červený", "čierny", "cesnak", "chlieb", "hrach" ];
var sk = StringComparer.Create(new CultureInfo("sk-SK"), false);
words.Sort(sk);
Console.WriteLine(string.Join(" ", words));
words.Sort(StringComparer.Ordinal);
Console.WriteLine(string.Join(" ", words));
The example sorts Slovak words. Slovak language has many accented characters
such as č or ň, and the ch digraph is a separate letter, which
follows h in the alphabet.
var sk = StringComparer.Create(new CultureInfo("sk-SK"), false);
words.Sort(sk);
The StringComparer.Create method creates a comparer for the
given culture; the second parameter tells whether to ignore case. Passing the
comparer is more explicit than changing the culture of the whole thread.
words.Sort(StringComparer.Ordinal);
For comparison, we also sort the words by the numeric values of their
characters. The words starting with č end up at the end and
chlieb is placed among the c words.
$ dotnet run auto banán cesnak cibuľa čaj čerešňa červený čierny čučoriedka drevo hrach chlieb auto banán cesnak chlieb cibuľa drevo hrach čaj čerešňa červený čierny čučoriedka
C# List sort by string length
The following example sorts the list of words by the words' length.
List<string> words = ["falcon", "order", "war",
"sky", "ocean", "blue", "cloud", "boy", "by", "raven",
"station", "battalion"];
words.Sort((a, b) => a.Length.CompareTo(b.Length));
Console.WriteLine(string.Join(",", words));
words.Sort((a, b) => b.Length.CompareTo(a.Length));
Console.WriteLine(string.Join(",", words));
We need to provide our own comparison method to do the job.
words.Sort((a, b) => a.Length.CompareTo(b.Length));
We provide a lambda expression to the Sort method. It uses the
CompareTo method of the integer type to compare the lengths of
the two words.
$ dotnet run by,war,sky,boy,blue,order,ocean,cloud,raven,falcon,station,battalion battalion,station,falcon,order,ocean,cloud,raven,blue,war,sky,boy,by
C# sort by string length with LINQ
The following example does the same thing with LINQ.
List<string> words = ["falcon", "order", "war",
"sky", "ocean", "blue", "cloud", "boy", "by", "raven",
"station", "battalion"];
var res = from word in words
orderby word.Length ascending
select word;
Console.WriteLine(string.Join(",", res));
var res2 = words.OrderByDescending(word => word.Length);
Console.WriteLine(string.Join(",", res2));
We sort the words by their length in ascending order with a query expression
and in descending order with the OrderByDescending method.
var res2 = words.OrderByDescending(word => word.Length);
The lambda expression is a key selector; it returns the value by which the elements are compared.
$ dotnet run by,war,sky,boy,blue,order,ocean,cloud,raven,falcon,station,battalion battalion,station,falcon,order,ocean,cloud,raven,blue,war,sky,boy,by
C# List sort names by surname
When we want to sort names by their surname, assuming that the whole name is a single string, we need to provide a custom comparison method.
List<string> names = ["John Doe", "Lucy Smith",
"Benjamin Young", "Robert Brown", "Thomas Moore",
"Linda Black", "Adam Smith", "Jane Smith"];
names.Sort((n1, n2) => string.Compare(n1.Split(' ')[1], n2.Split(' ')[1],
StringComparison.Ordinal));
names.ForEach(Console.WriteLine);
We split each string into two parts and compare the second parts in a lambda expression.
names.Sort((n1, n2) => string.Compare(n1.Split(' ')[1], n2.Split(' ')[1],
StringComparison.Ordinal));
The string.Compare method with
StringComparison.Ordinal performs a fast, culture-independent
comparison.
$ dotnet run Linda Black Robert Brown John Doe Thomas Moore Lucy Smith Adam Smith Jane Smith Benjamin Young
C# sort names by surname with LINQ
The following example provides a LINQ solution.
List<string> names = ["John Doe", "Lucy Smith",
"Benjamin Young", "Robert Brown", "Thomas Moore",
"Linda Black", "Adam Smith", "Jane Smith"];
var res = from name in names
orderby name.Split(' ')[1]
select name;
foreach (var name in res)
{
Console.WriteLine(name);
}
The example sorts the names by their surname using the query expression
syntax. Note that the order of the Smiths is kept. With LINQ, this is
guaranteed, because OrderBy is a stable sort. The
List.Sort version above happened to produce the same result; for
small lists, introsort switches to insertion sort, but this is an
implementation detail we must not rely on.
$ dotnet run Linda Black Robert Brown John Doe Thomas Moore Lucy Smith Adam Smith Jane Smith Benjamin Young
C# List.Sort is not stable
The following example shows the difference between the unstable
List.Sort and the stable OrderBy.
var items = Enumerable.Range(0, 100)
.Select(i => (Key: i % 3, Seq: i))
.ToList();
var linq = items.OrderBy(e => e.Key).ToList();
var sorted = new List<(int Key, int Seq)>(items);
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
Console.WriteLine($"OrderBy keeps order: {IsStable(linq)}");
Console.WriteLine($"List.Sort keeps order: {IsStable(sorted)}");
var stableSort = new List<(int Key, int Seq)>(items);
stableSort.Sort((a, b) =>
{
int res = a.Key.CompareTo(b.Key);
return res != 0 ? res : a.Seq.CompareTo(b.Seq);
});
Console.WriteLine($"List.Sort with tie-breaker: {IsStable(stableSort)}");
static bool IsStable(List<(int Key, int Seq)> data)
{
for (int i = 1; i < data.Count; i++)
{
if (data[i].Key == data[i - 1].Key && data[i].Seq < data[i - 1].Seq)
{
return false;
}
}
return true;
}
We have one hundred elements with only three distinct keys. The
Seq field records the original position of each element.
static bool IsStable(List<(int Key, int Seq)> data)
The IsStable function checks whether the elements with equal
keys are still in their original order.
var linq = items.OrderBy(e => e.Key).ToList();
The stable OrderBy keeps the original order of equal elements.
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
The List.Sort method rearranges elements with equal keys.
stableSort.Sort((a, b) =>
{
int res = a.Key.CompareTo(b.Key);
return res != 0 ? res : a.Seq.CompareTo(b.Seq);
});
If we need a stable in-place sort, we can add the original position as a tie-breaker. Then no two elements compare as equal.
$ dotnet run OrderBy keeps order: True List.Sort keeps order: False List.Sort with tie-breaker: True
C# List sort case insensitive
To compare strings in a case-insensitive manner, we can use the built-in
StringComparer.OrdinalIgnoreCase.
List<string> words =
[
"world", "War", "abbot", "Caesar", "castle", "sky", "den",
"forest", "ocean", "water", "falcon", "owl", "rain", "Earth"
];
words.Sort(StringComparer.Ordinal);
Console.WriteLine(string.Join(" ", words));
words.Sort(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(string.Join(" ", words));
The example sorts a list of words in case-sensitive ordinal order and in case-insensitive order.
words.Sort(StringComparer.Ordinal);
In ordinal comparison, all uppercase ASCII letters come before lowercase letters.
words.Sort(StringComparer.OrdinalIgnoreCase);
The OrdinalIgnoreCase comparer treats uppercase and lowercase
letters as equal.
$ dotnet run Caesar Earth War abbot castle den falcon forest ocean owl rain sky water world abbot Caesar castle den Earth falcon forest ocean owl rain sky War water world
C# List sort with Comparison
The Comparison<T> delegate represents the method that
compares two objects of the same type.
List<(string Name, int Salary)> employees =
[
("John Doe", 1230),
("Adam Novak", 670),
("Robin Brown", 2300),
("Rowan Cruise", 990),
("Joe Draker", 1190),
("Janet Doe", 980),
("Lucy Smith", 980),
("Thomas Moore", 1400)
];
employees.Sort(delegate ((string Name, int Salary) e1, (string Name, int Salary) e2)
{
return e1.Salary.CompareTo(e2.Salary);
});
Console.WriteLine(string.Join(Environment.NewLine, employees));
We sort a list of employees using an anonymous method. The employees are sorted by salaries in ascending order.
employees.Sort(delegate ((string Name, int Salary) e1, (string Name, int Salary) e2)
{
return e1.Salary.CompareTo(e2.Salary);
});
The delegate keyword creates an anonymous method that is
converted to the Comparison delegate.
$ dotnet run (Adam Novak, 670) (Janet Doe, 980) (Lucy Smith, 980) (Rowan Cruise, 990) (Joe Draker, 1190) (John Doe, 1230) (Thomas Moore, 1400) (Robin Brown, 2300)
C# List sort with lambda expression
Lambda expressions are a shorter way of creating anonymous methods.
List<(string Name, int Salary)> employees =
[
("John Doe", 1230),
("Adam Novak", 670),
("Robin Brown", 2300),
("Rowan Cruise", 990),
("Joe Draker", 1190),
("Janet Doe", 980),
("Lucy Smith", 980),
("Thomas Moore", 1400)
];
employees.Sort((e1, e2) => e2.Salary.CompareTo(e1.Salary));
Console.WriteLine(string.Join(Environment.NewLine, employees));
The employees are sorted by salaries in descending order.
employees.Sort((e1, e2) => e2.Salary.CompareTo(e1.Salary));
The parameter types are inferred from the type of the list.
$ dotnet run (Robin Brown, 2300) (Thomas Moore, 1400) (John Doe, 1230) (Joe Draker, 1190) (Rowan Cruise, 990) (Janet Doe, 980) (Lucy Smith, 980) (Adam Novak, 670)
C# List sort with IComparable
The IComparable<T> interface defines a type-specific
comparison method that a value type or class implements to order or sort its
instances. The Sort method without arguments uses it.
The IComparable interface is best suited for smaller, compact
types for which the sorting order is obvious.
List<Employee> employees =
[
new("John Doe", 1230),
new("Adam Novak", 670),
new("Robin Brown", 2300),
new("Rowan Cruise", 990),
new("Joe Draker", 1190),
new("Janet Doe", 980),
new("Lucy Smith", 980),
new("Thomas Moore", 1400)
];
employees.Sort();
Console.WriteLine(string.Join(Environment.NewLine, employees));
record Employee(string Name, int Salary) : IComparable<Employee>
{
public int CompareTo(Employee? other)
{
if (other is null) return 1;
return Salary.CompareTo(other.Salary);
}
}
We have an Employee record with a built-in comparison method
which sorts the employees by their salary in ascending order.
public int CompareTo(Employee? other)
{
if (other is null) return 1;
return Salary.CompareTo(other.Salary);
}
By convention, any instance is greater than null. Then we
compare the salaries.
$ dotnet run
Employee { Name = Adam Novak, Salary = 670 }
Employee { Name = Janet Doe, Salary = 980 }
Employee { Name = Lucy Smith, Salary = 980 }
Employee { Name = Rowan Cruise, Salary = 990 }
Employee { Name = Joe Draker, Salary = 1190 }
Employee { Name = John Doe, Salary = 1230 }
Employee { Name = Thomas Moore, Salary = 1400 }
Employee { Name = Robin Brown, Salary = 2300 }
C# List sort with IComparer
The IComparer<T> interface defines a method that compares
two objects. It is implemented by a separate class, not by the sorted type.
With IComparer we have more flexibility; we can define multiple
comparers or update existing ones without touching the type itself. It also
separates the sorting logic from the type.
List<(string Name, int Salary)> employees =
[
("John Doe", 1230),
("Adam Novak", 670),
("Robin Brown", 2300),
("Rowan Cruise", 990),
("Joe Draker", 1190),
("Janet Doe", 980),
("Lucy Smith", 980),
("Thomas Moore", 1400)
];
employees.Sort(new SurnameComparer());
employees.ForEach(employee => Console.WriteLine(employee));
class SurnameComparer : IComparer<(string Name, int Salary)>
{
public int Compare((string Name, int Salary) e1, (string Name, int Salary) e2)
{
int res = string.Compare(e1.Name.Split(' ')[1], e2.Name.Split(' ')[1],
StringComparison.Ordinal);
return res != 0 ? res : string.Compare(e1.Name, e2.Name, StringComparison.Ordinal);
}
}
We sort the employees by their surnames.
int res = string.Compare(e1.Name.Split(' ')[1], e2.Name.Split(' ')[1],
StringComparison.Ordinal);
return res != 0 ? res : string.Compare(e1.Name, e2.Name, StringComparison.Ordinal);
If the surnames are equal, we compare the full names. This way the result does not depend on the unstable sort; the two Does are always ordered by their first names.
$ dotnet run (Robin Brown, 2300) (Rowan Cruise, 990) (Janet Doe, 980) (John Doe, 1230) (Joe Draker, 1190) (Thomas Moore, 1400) (Adam Novak, 670) (Lucy Smith, 980)
C# Comparer.Create
Writing a class for each comparer is verbose. The
Comparer<T>.Create method creates an
IComparer<T> from a lambda expression. This is useful for
APIs which accept only comparers, such as SortedSet.
List<string> words = ["falcon", "Sky", "owl", "cloud", "Ocean", "by", "rain"];
var byLength = Comparer<string>.Create((a, b) => a.Length.CompareTo(b.Length));
var byLastChar = Comparer<string>.Create((a, b) => a[^1].CompareTo(b[^1]));
words.Sort(byLength);
Console.WriteLine(string.Join(" ", words));
words.Sort(byLastChar);
Console.WriteLine(string.Join(" ", words));
var reverse = Comparer<int>.Create((a, b) => b.CompareTo(a));
var set = new SortedSet<int>([5, 1, 9, 3, 7], reverse);
Console.WriteLine(string.Join(" ", set));
We create comparers that sort words by length and by their last character.
var byLastChar = Comparer<string>.Create((a, b) => a[^1].CompareTo(b[^1]));
The ^1 index refers to the last character of the string.
var set = new SortedSet<int>([5, 1, 9, 3, 7], reverse);
The SortedSet keeps its elements in the order given by the
comparer; here in descending order.
$ dotnet run by Sky owl rain cloud Ocean falcon cloud owl rain Ocean falcon by Sky 9 7 5 3 1
C# List sort range
A portion of the list can be sorted with the
Sort(Int32, Int32, IComparer<T>) overload.
List<int> nums = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
nums.Sort(2, 5, Comparer<int>.Default);
Console.WriteLine(string.Join(" ", nums));
We sort five elements starting at index 2. The rest of the list is not touched.
nums.Sort(2, 5, Comparer<int>.Default);
The Comparer<int>.Default property returns the default
comparer of the type.
$ dotnet run 9 8 3 4 5 6 7 2 1 0
C# List sort tuples
The following example sorts a list of tuples.
List<(string Name, int Grade)> data =
[
("Patrick", 89),
("Lucia", 92),
("Veronika", 72),
("Robert", 78),
("Maria", 65),
("Andrea", 51),
("Ondrej", 45)
];
data.Sort((s1, s2) => s1.Grade.CompareTo(s2.Grade));
Console.WriteLine(string.Join(", ", data));
data.Sort((s1, s2) => s2.Grade.CompareTo(s1.Grade));
Console.WriteLine(string.Join(", ", data));
data.Sort();
Console.WriteLine(string.Join(", ", data));
We have a list of tuples representing students and their grades. We sort the tuples by grade in ascending and descending order.
data.Sort();
Value tuples have a default ordering: they are compared element by element. Without a comparison, the students are sorted by name.
$ dotnet run (Ondrej, 45), (Andrea, 51), (Maria, 65), (Veronika, 72), (Robert, 78), (Patrick, 89), (Lucia, 92) (Lucia, 92), (Patrick, 89), (Robert, 78), (Veronika, 72), (Maria, 65), (Andrea, 51), (Ondrej, 45) (Andrea, 51), (Lucia, 92), (Maria, 65), (Ondrej, 45), (Patrick, 89), (Robert, 78), (Veronika, 72)
C# List sort objects
In the following example, we sort a list of User objects.
List<User> users = [
new("John", "Doe", 1230),
new("Lucy", "Novak", 670),
new("Ben", "Walter", 2050),
new("Robin", "Brown", 2300),
new("Joe", "Draker", 1190),
new("Janet", "Doe", 980),
];
users.Sort((u1, u2) => string.Compare(u1.LastName, u2.LastName,
StringComparison.Ordinal));
users.ForEach(Console.WriteLine);
record User(string FirstName, string LastName, int Salary);
We have a list of user objects. The user has three attributes: first name, last name, and salary. We sort the list by users' last names.
users.Sort((u1, u2) => string.Compare(u1.LastName, u2.LastName,
StringComparison.Ordinal));
In the lambda expression, we compare the LastName property of the
two elements.
$ dotnet run
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
C# sort objects with LINQ
Next we sort users by their salaries.
List<User> users = [
new("John", "Doe", 1230),
new("Lucy", "Novak", 670),
new("Ben", "Walter", 2050),
new("Robin", "Brown", 2300),
new("Joe", "Draker", 1190),
new("Janet", "Doe", 980),
];
Console.WriteLine("sort ascending by salary");
var res1 = from user in users
orderby user.Salary
select user;
foreach (var e in res1)
{
Console.WriteLine(e);
}
Console.WriteLine("sort descending by salary");
var res2 = from user in users
orderby user.Salary descending
select user;
foreach (var e in res2)
{
Console.WriteLine(e);
}
record User(string FirstName, string LastName, int Salary);
The example sorts a list of user objects by users' salaries. It uses the LINQ query syntax.
$ dotnet run
sort ascending by salary
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
sort descending by salary
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
C# List sort dates
In the following example, we sort a list of users by their birthdays.
List<User> users = [
new("John", "Doe", new DateOnly(1983, 9, 4)),
new("Lucy", "Novak", new DateOnly(1978, 11, 18)),
new("Ben", "Walter", new DateOnly(1998, 12, 1)),
new("Robin", "Brown", new DateOnly(2001, 2, 14)),
new("Joe", "Draker", new DateOnly(1980, 1, 10)),
new("Janet", "Doe", new DateOnly(1967, 8, 23)),
];
Console.WriteLine("sort ascending by birthday");
users.Sort((u1, u2) => u1.Birthday.CompareTo(u2.Birthday));
users.ForEach(Console.WriteLine);
Console.WriteLine("sort descending by birthday");
foreach (var u in users.OrderByDescending(e => e.Birthday))
{
Console.WriteLine(u);
}
record User(string FirstName, string LastName, DateOnly Birthday)
{
public override string ToString() =>
$"{FirstName} {LastName}, {Birthday:yyyy-MM-dd}";
}
We sort the list of users by their birthdays using the Sort
method and the OrderByDescending method.
users.Sort((u1, u2) => u1.Birthday.CompareTo(u2.Birthday));
The DateOnly type implements IComparable; earlier
dates are less than later ones. The same applies to DateTime.
public override string ToString() =>
$"{FirstName} {LastName}, {Birthday:yyyy-MM-dd}";
We override the ToString method to print the dates in a
culture-independent format.
$ dotnet run sort ascending by birthday Janet Doe, 1967-08-23 Lucy Novak, 1978-11-18 Joe Draker, 1980-01-10 John Doe, 1983-09-04 Ben Walter, 1998-12-01 Robin Brown, 2001-02-14 sort descending by birthday Robin Brown, 2001-02-14 Ben Walter, 1998-12-01 John Doe, 1983-09-04 Joe Draker, 1980-01-10 Lucy Novak, 1978-11-18 Janet Doe, 1967-08-23
C# List sort objects by multiple fields
The following example sorts user objects by multiple fields.
List<User> users = [
new("John", "Doe", 1230),
new("Lucy", "Novak", 670),
new("Ben", "Walter", 2050),
new("Robin", "Brown", 2300),
new("Amy", "Doe", 1250),
new("Joe", "Draker", 1190),
new("Janet", "Doe", 980),
new("Albert", "Novak", 1930),
];
users.Sort((u1, u2) =>
{
int result = string.Compare(u1.LastName, u2.LastName, StringComparison.Ordinal);
return result == 0 ? u1.Salary.CompareTo(u2.Salary) : result;
});
Console.WriteLine("sort ascending by last name and salary");
foreach (var user in users)
{
Console.WriteLine(user);
}
record User(string FirstName, string LastName, int Salary);
We sort users first by last names and then by salaries.
users.Sort((u1, u2) =>
{
int result = string.Compare(u1.LastName, u2.LastName, StringComparison.Ordinal);
return result == 0 ? u1.Salary.CompareTo(u2.Salary) : result;
});
First, the users are compared by their LastName property. If the
comparison returns 0, that is, their last names are equal, we compare their
salaries.
$ dotnet run
sort ascending by last name and salary
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Amy, LastName = Doe, Salary = 1250 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Albert, LastName = Novak, Salary = 1930 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
C# sort by multiple fields with LINQ
The next example sorts by multiple fields with LINQ methods.
List<User> users = [
new("John", "Doe", 1230),
new("Lucy", "Novak", 670),
new("Ben", "Walter", 2050),
new("Robin", "Brown", 2300),
new("Amy", "Doe", 1250),
new("Joe", "Draker", 1190),
new("Janet", "Doe", 980),
new("Albert", "Novak", 1930),
];
Console.WriteLine("sort by last name ascending, salary descending");
var res = users.OrderBy(u => u.LastName).ThenByDescending(u => u.Salary);
foreach (var user in res)
{
Console.WriteLine(user);
}
record User(string FirstName, string LastName, int Salary);
The users are sorted with the OrderBy and
ThenByDescending methods.
var res = users.OrderBy(u => u.LastName).ThenByDescending(u => u.Salary);
The ThenBy and ThenByDescending methods define
secondary sort keys; each key may have its own direction. Here the last
names are ascending and the salaries descending.
$ dotnet run
sort by last name ascending, salary descending
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = Amy, LastName = Doe, Salary = 1250 }
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Janet, LastName = Doe, Salary = 980 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
User { FirstName = Albert, LastName = Novak, Salary = 1930 }
User { FirstName = Lucy, LastName = Novak, Salary = 670 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
C# List sort by rating
Imagine that we have a rating system which cannot be sorted alphabetically. For instance, rating can have values such as C, C+, C-. One solution is to use enumerations.
List<Product> products = [
new() { Name = "Product A", ProdRat = Rating.A },
new() { Name = "Product B", ProdRat = Rating.AMinus },
new() { Name = "Product C", ProdRat = Rating.B },
new() { Name = "Product D", ProdRat = Rating.APlus },
new() { Name = "Product E", ProdRat = Rating.D },
new() { Name = "Product F", ProdRat = Rating.C },
new() { Name = "Product G", ProdRat = Rating.CMinus },
new() { Name = "Product H", ProdRat = Rating.CPlus },
new() { Name = "Product I", ProdRat = Rating.BMinus },
];
Console.WriteLine("sorted by rating ascending");
products.Sort((p1, p2) => p1.ProdRat.CompareTo(p2.ProdRat));
foreach (var product in products)
{
Console.WriteLine(product);
}
Console.WriteLine("sorted by rating descending");
products.Sort((p1, p2) => p2.ProdRat.CompareTo(p1.ProdRat));
foreach (var product in products)
{
Console.WriteLine(product);
}
enum Rating
{
D,
DPlus,
CMinus,
C,
CPlus,
BMinus,
B,
BPlus,
AMinus,
A,
APlus
}
class Product
{
private static readonly Dictionary<Rating, string> ratings = new()
{
[Rating.APlus] = "A+", [Rating.A] = "A", [Rating.AMinus] = "A-",
[Rating.BPlus] = "B+", [Rating.B] = "B", [Rating.BMinus] = "B-",
[Rating.CPlus] = "C+", [Rating.C] = "C", [Rating.CMinus] = "C-",
[Rating.DPlus] = "D+", [Rating.D] = "D"
};
public required string Name { get; init; }
public Rating ProdRat { get; init; }
public override string ToString()
{
return $"{Name} has rating {ratings[ProdRat]}";
}
}
We sort products by their ratings.
products.Sort((p1, p2) => p1.ProdRat.CompareTo(p2.ProdRat));
In the lambda expression, we compare the product rating enumerations with
CompareTo.
enum Rating
{
D,
DPlus,
CMinus,
C,
CPlus,
BMinus,
B,
BPlus,
AMinus,
A,
APlus
}
We have an enumeration of ratings. Internally, the members are given integer
values in the order of declaration: Rating.D has the lowest value
and Rating.APlus the highest. The order of the declaration
therefore defines the sort order.
private static readonly Dictionary<Rating, string> ratings = new()
In this dictionary, we assign string representations to the rating enumerations. It is static, since all products share it.
$ dotnet run sorted by rating ascending Product E has rating D Product G has rating C- Product F has rating C Product H has rating C+ Product I has rating B- Product C has rating B Product B has rating A- Product A has rating A Product D has rating A+ sorted by rating descending Product D has rating A+ Product A has rating A Product B has rating A- Product C has rating B Product I has rating B- Product H has rating C+ Product F has rating C Product G has rating C- Product E has rating D
C# List sort cards
In the next example, we sort cards. The Card class implements
the IComparable interface. The interface is used in cases where
a natural ordering of objects exists.
In Poker and similar card games, the card value is determined by its rank,
and if needed, by its suit. Since this ordering is natural for cards, we can
use the IComparable interface.
List<Card> cards =
[
new(Rank.King, Suit.Diamonds),
new(Rank.Five, Suit.Hearts),
new(Rank.Ace, Suit.Clubs),
new(Rank.Nine, Suit.Spades),
new(Rank.Jack, Suit.Spades),
new(Rank.Jack, Suit.Diamonds)
];
cards.Sort();
foreach (var card in cards)
{
Console.WriteLine(card);
}
enum Rank
{
Two, Three, Four, Five, Six, Seven, Eight,
Nine, Ten, Jack, Queen, King, Ace
}
enum Suit
{
Clubs,
Diamonds,
Hearts,
Spades
}
class Card(Rank rank, Suit suit) : IComparable<Card>
{
public Rank Rank { get; } = rank;
public Suit Suit { get; } = suit;
public int CompareTo(Card? other)
{
if (other is null) return 1;
var index = Rank.CompareTo(other.Rank);
if (index == 0) index = Suit.CompareTo(other.Suit);
return index;
}
public override string ToString()
{
return $"{Rank} of {Suit}";
}
}
We have a list of six cards that we want to sort.
class Card(Rank rank, Suit suit) : IComparable<Card>
The Card class implements the IComparable
interface. This forces us to implement its CompareTo method.
var index = Rank.CompareTo(other.Rank); if (index == 0) index = Suit.CompareTo(other.Suit);
In the CompareTo method, we first compare the rank of the card.
If the rank is the same, we compare the suit.
cards.Sort();
Without any parameters, the Sort method calls the
CompareTo method of the elements. We have two cards of the same
rank, two Jacks. The Jack of Spades is greater than the Jack of Diamonds,
because Spades come later in the Suit enumeration.
$ dotnet run Five of Hearts Nine of Spades Jack of Diamonds Jack of Spades King of Diamonds Ace of Clubs
Source
In this article we sorted list elements in C# language.
Author
List all C# tutorials.