/commands/csharp/sort-list-by-property-linq
c# sort a list of objects by property (OrderBy, ThenBy, List.Sort)
- tool
- C#
- versions
- .NET Framework 3.5+ (LINQ), Order(): .NET 7+
- os
- any
list.OrderBy(p => p.Age) sorts a list of objects by a property with LINQ and returns a new sequence; add .ToList() to get a List<T>. Use OrderByDescending for the reverse order and ThenBy/ThenByDescending for a second key. To sort the existing list in place, call list.Sort((a, b) => a.Age.CompareTo(b.Age)).
Examples
cat > sort.cs <<'EOF'
List<Person> people =
[
new("zoe", 31, "ops"),
new("adam", 25, "dev"),
new("Bea", 31, "dev"),
new("Carl", 19, "ops"),
];
static string Names(IEnumerable<Person> ps) => string.Join(", ", ps.Select(p => p.Name));
Console.WriteLine("by age: " + Names(people.OrderBy(p => p.Age)));
Console.WriteLine("by age desc: " + Names(people.OrderByDescending(p => p.Age)));
Console.WriteLine("team, then name: " + Names(people.OrderBy(p => p.Team).ThenBy(p => p.Name)));
Console.WriteLine("ordinal name: " + Names(people.OrderBy(p => p.Name, StringComparer.Ordinal)));
Console.WriteLine("ignore case: " + Names(people.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase)));
Console.WriteLine("list unchanged: " + Names(people));
people.Sort((a, b) => a.Age.CompareTo(b.Age));
Console.WriteLine("after Sort(): " + Names(people));
record Person(string Name, int Age, string Team);
EOF
dotnet run sort.csOutput
by age: Carl, adam, zoe, Bea by age desc: zoe, Bea, adam, Carl team, then name: adam, Bea, Carl, zoe ordinal name: Bea, Carl, adam, zoe ignore case: adam, Bea, Carl, zoe list unchanged: zoe, adam, Bea, Carl after Sort(): Carl, adam, zoe, Bea
OrderBy keeps zoe before Bea when both are 31, because it is stable. The ordinal comparer sorts by character code, so capitalised names come before adam; OrdinalIgnoreCase gives alphabetical order regardless of case. Only Sort() changes people itself.
Options
| Code | Result |
|---|---|
| list.OrderBy(x => x.Key).ToList() | New list sorted ascending |
| list.OrderByDescending(x => x.Key) | Descending |
| .OrderBy(x => x.A).ThenBy(x => x.B) | Sort by A, then by B for equal A values |
| .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase) | Case-insensitive string order |
| list.Sort((a, b) => a.Key.CompareTo(b.Key)) | In place, not stable |
| numbers.Order() / OrderDescending() | Sort the elements themselves (.NET 7+) |
| from p in people orderby p.Age descending select p | Query syntax, same as OrderByDescending |
Pitfalls
- Discarded result.
people.OrderBy(p => p.Age);on its own line does nothing visible: the sorted sequence is thrown away. Assign it, or useSort. - OrderBy twice.
.OrderBy(a).OrderBy(b)sorts bybonly (stably, soasurvives as a tie-breaker by accident). UseThenByfor secondary keys. - Null keys sort first in ascending order with the default comparer; a
nullelement itself causes aNullReferenceExceptioninside the key selector. - Missing LINQ. Without implicit usings,
OrderByneedsusing System.Linq;; otherwise the compiler reports CS1061 does not contain a definition for OrderBy. - Record or class? Sorting works the same for both; see record vs class for which to use for data like this.
Confirm it worked
Print the key you sorted by next to each item and check the first and last values, as in the example. For a sorted list, list.Zip(list.Skip(1)).All(t => t.First.Age <= t.Second.Age) returns true.
FAQ
Does OrderBy change the original list?
No. OrderBy returns a new, lazily evaluated sequence; the list keeps its order. Call ToList() to store the result, or use List.Sort to sort in place.
Is OrderBy stable?
Yes. The documentation states that OrderBy performs a stable sort: elements with equal keys keep their original order. List.Sort is not stable.
How do I sort strings case-insensitively?
Pass a comparer: people.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase). For culture-aware ordering use StringComparer.CurrentCultureIgnoreCase or a specific CultureInfo.