Codectionary / Developer documentation / C#

LINQ Ordering & Grouping

OrderBy() and OrderByDescending() sort a sequence by a key you specify, with ThenBy() for secondary sort keys on ties. GroupBy() splits a sequence into groups based on a key selector, producing a sequence of groups where each group itself is enumerable and has a Key property identifying which group it represents.

Syntax

collection.OrderBy(keySelector).GroupBy(keySelector)

Examples

OrderBy and OrderByDescending

Sorting a sequence by a chosen key.

List<int> numbers = new List<int> { 5, 1, 4, 2, 3 };

var ascending = numbers.OrderBy(n => n).ToList();
Console.WriteLine(string.Join(", ", ascending));  // 1, 2, 3, 4, 5

var descending = numbers.OrderByDescending(n => n).ToList();
Console.WriteLine(string.Join(", ", descending));  // 5, 4, 3, 2, 1

Multi-Level Sorting with ThenBy

Sorting by a primary key, then breaking ties with a secondary key.

record Person(string Department, string Name);

List<Person> people = new List<Person>
{
    new Person("Sales", "Fola"),
    new Person("Engineering", "Zain"),
    new Person("Engineering", "Amir")
};

var sorted = people
    .OrderBy(p => p.Department)
    .ThenBy(p => p.Name)
    .ToList();

foreach (var p in sorted) Console.WriteLine($"{p.Department}: {p.Name}");
// Engineering: Amir, Engineering: Zain, Sales: Fola

GroupBy(): Grouping Elements

Splitting a sequence into groups based on a key.

record Student(string Name, string Grade);

List<Student> students = new List<Student>
{
    new Student("Fola", "A"),
    new Student("Zain", "B"),
    new Student("Jamal", "A")
};

var byGrade = students.GroupBy(s => s.Grade);

foreach (var group in byGrade)
{
    Console.WriteLine($"Grade {group.Key}: {string.Join(", ", group.Select(s => s.Name))}");
}
// Grade A: Fola, Jamal
// Grade B: Zain

Best practices

  • Use ThenBy()/ThenByDescending() for secondary sort criteria instead of trying to encode multiple sort keys into one comparison
  • Remember each GroupBy() group is itself an IEnumerable<T> with a Key property - iterate or call .ToList() on it just like any other sequence
  • Combine GroupBy() with Select() to project each group into a summary (like a count or average) rather than keeping the full group
  • OrderBy() returns a new ordered sequence rather than sorting in place - unlike List<T>.Sort(), the original collection is untouched

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

Lambda Expressions
A lambda expression is a compact way to represent an anonymous function - a block of code that can be passed around as a value, using the => (goes to) operator. Lambdas work with delegate types and functional interfaces like Func<> and Action<>, letting you write inline logic without a full named method. Lambdas are the foundation that makes LINQ's fluent, expressive query syntax possible.
LINQ Basics: Where & Select
LINQ (Language Integrated Query) provides a consistent, declarative way to query collections, databases, and XML directly within C# syntax. Where() filters a sequence down to elements matching a condition, and Select() transforms each element into something new (a projection). Both are 'lazy' - they build a query that only actually runs when you iterate over the result or call something like ToList().
LINQ Aggregation: Sum, Count, Average
LINQ provides built-in aggregation methods that reduce a sequence down to a single summary value: Count() for the number of elements, Sum() for a total, Average() for the mean, and Max()/Min() for extremes. Aggregate() is the general-purpose version, letting you define fully custom accumulation logic when the built-in methods aren't enough.
LINQ Element & Quantifier Operators
Element operators retrieve a single item from a sequence: First()/FirstOrDefault() for the first match, Single()/SingleOrDefault() for exactly one match, with 'OrDefault' variants returning a default value instead of throwing when nothing matches. Quantifier operators return a bool describing the sequence as a whole: Any() checks if at least one element matches, and All() checks if every element matches.