Skip to main content

.NET LINQ Ordering

When working with data collections in C#, sorting and ordering elements is a fundamental operation. LINQ (Language Integrated Query) provides powerful operators that make ordering data straightforward and flexible. In this guide, we'll explore LINQ's ordering capabilities and learn how to use them effectively.

Introduction to LINQ Ordering

LINQ offers several methods to order sequences:

  • OrderBy - Orders elements in ascending order
  • OrderByDescending - Orders elements in descending order
  • ThenBy - Performs a secondary sort in ascending order
  • ThenByDescending - Performs a secondary sort in descending order

These methods let you sort collections based on one or more properties, enabling everything from simple to complex sorting scenarios.

Basic Ordering with OrderBy

The most basic ordering operation is sorting a collection in ascending order using OrderBy.

csharp
using System;
using System.Linq;
using System.Collections.Generic;

// Sample data
List<int> numbers = new List<int> { 5, 3, 9, 1, 7, 2 };

// Order the numbers in ascending order
var orderedNumbers = numbers.OrderBy(n => n);

// Display the result
Console.WriteLine("Original numbers:");
foreach (var num in numbers)
{
Console.Write($"{num} ");
}

Console.WriteLine("\nOrdered numbers:");
foreach (var num in orderedNumbers)
{
Console.Write($"{num} ");
}

Output:

Original numbers:
5 3 9 1 7 2
Ordered numbers:
1 2 3 5 7 9

The lambda expression n => n tells LINQ to use the value itself as the sorting key. The original collection remains unchanged, and a new ordered sequence is created.

Descending Order with OrderByDescending

To sort in descending order, use OrderByDescending:

csharp
// Order the numbers in descending order
var descendingNumbers = numbers.OrderByDescending(n => n);

Console.WriteLine("\nDescending order:");
foreach (var num in descendingNumbers)
{
Console.Write($"{num} ");
}

Output:

Descending order:
9 7 5 3 2 1

Ordering Objects by Properties

LINQ shines when working with complex objects. Let's see how to order a collection of Person objects:

csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }

public override string ToString()
{
return $"{Name} (Age: {Age})";
}
}

// Sample data
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 32 },
new Person { Name = "Charlie", Age = 19 },
new Person { Name = "Diana", Age = 25 }
};

// Order people by age (ascending)
var peopleByAge = people.OrderBy(p => p.Age);

Console.WriteLine("People ordered by age:");
foreach (var person in peopleByAge)
{
Console.WriteLine(person);
}

Output:

People ordered by age:
Charlie (Age: 19)
Alice (Age: 25)
Diana (Age: 25)
Bob (Age: 32)

Notice that LINQ maintains the original order for items with equal keys (Alice appears before Diana because that was their original order).

Multiple Level Ordering with ThenBy and ThenByDescending

When items have the same primary sorting value (like the same age), you might want to add a secondary sort criterion. That's where ThenBy and ThenByDescending come in:

csharp
// Order people by age, then by name
var orderedPeople = people
.OrderBy(p => p.Age)
.ThenBy(p => p.Name);

Console.WriteLine("\nPeople ordered by age, then by name:");
foreach (var person in orderedPeople)
{
Console.WriteLine(person);
}

Output:

People ordered by age, then by name:
Charlie (Age: 19)
Alice (Age: 25)
Diana (Age: 25)
Bob (Age: 32)

Now, Alice and Diana (both age 25) are arranged alphabetically by name.

You can chain multiple ThenBy or ThenByDescending calls for additional sorting levels:

csharp
public class Student
{
public string Name { get; set; }
public string Grade { get; set; }
public int Score { get; set; }

public override string ToString()
{
return $"{Name} - Grade: {Grade}, Score: {Score}";
}
}

List<Student> students = new List<Student>
{
new Student { Name = "Alice", Grade = "A", Score = 95 },
new Student { Name = "Bob", Grade = "B", Score = 85 },
new Student { Name = "Charlie", Grade = "A", Score = 92 },
new Student { Name = "Diana", Grade = "B", Score = 88 },
new Student { Name = "Eva", Grade = "A", Score = 95 }
};

// Order by grade, then by score (descending), then by name
var orderedStudents = students
.OrderBy(s => s.Grade)
.ThenByDescending(s => s.Score)
.ThenBy(s => s.Name);

Console.WriteLine("Ordered students:");
foreach (var student in orderedStudents)
{
Console.WriteLine(student);
}

Output:

Ordered students:
Alice - Grade: A, Score: 95
Eva - Grade: A, Score: 95
Charlie - Grade: A, Score: 92
Diana - Grade: B, Score: 88
Bob - Grade: B, Score: 85

Ordering with Custom Comparers

Sometimes you need custom sorting logic. LINQ's ordering methods accept custom IComparer<T> implementations:

csharp
public class LastNameComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// Extract last names (assuming format is "FirstName LastName")
string lastNameX = x.Split(' ').Last();
string lastNameY = y.Split(' ').Last();

return string.Compare(lastNameX, lastNameY);
}
}

// Sample data
List<string> fullNames = new List<string>
{
"John Smith",
"Alice Johnson",
"Robert Brown",
"Sarah Davis"
};

// Order by last name using custom comparer
var orderedByLastName = fullNames.OrderBy(name => name, new LastNameComparer());

Console.WriteLine("Names ordered by last name:");
foreach (var name in orderedByLastName)
{
Console.WriteLine(name);
}

Output:

Names ordered by last name:
Robert Brown
Sarah Davis
Alice Johnson
John Smith

Real-World Application: Sorting Products in an E-commerce System

Let's look at a practical example of using LINQ ordering in an e-commerce scenario:

csharp
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Rating { get; set; }
public DateTime ReleaseDate { get; set; }

public override string ToString()
{
return $"{Name} - ${Price} (Rating: {Rating}, Released: {ReleaseDate.ToShortDateString()})";
}
}

// Sample product catalog
List<Product> products = new List<Product>
{
new Product { Name = "Laptop", Price = 1200.00m, Rating = 4, ReleaseDate = new DateTime(2022, 5, 15) },
new Product { Name = "Smartphone", Price = 800.00m, Rating = 5, ReleaseDate = new DateTime(2023, 1, 10) },
new Product { Name = "Headphones", Price = 150.00m, Rating = 5, ReleaseDate = new DateTime(2022, 8, 20) },
new Product { Name = "Tablet", Price = 400.00m, Rating = 3, ReleaseDate = new DateTime(2021, 11, 5) },
new Product { Name = "Smartwatch", Price = 250.00m, Rating = 4, ReleaseDate = new DateTime(2023, 2, 14) }
};

// 1. Sort by price (lowest first) - Budget-conscious shoppers
var budgetFriendly = products.OrderBy(p => p.Price);
Console.WriteLine("Budget-friendly sorting:");
foreach (var product in budgetFriendly)
{
Console.WriteLine(product);
}

// 2. Sort by rating, then by price - Best value shoppers
var bestValue = products
.OrderByDescending(p => p.Rating)
.ThenBy(p => p.Price);
Console.WriteLine("\nBest value sorting:");
foreach (var product in bestValue)
{
Console.WriteLine(product);
}

// 3. Sort by newest first - Trend followers
var newest = products.OrderByDescending(p => p.ReleaseDate);
Console.WriteLine("\nNewest products first:");
foreach (var product in newest)
{
Console.WriteLine(product);
}

Output:

Budget-friendly sorting:
Headphones - $150.00 (Rating: 5, Released: 8/20/2022)
Smartwatch - $250.00 (Rating: 4, Released: 2/14/2023)
Tablet - $400.00 (Rating: 3, Released: 11/5/2021)
Smartphone - $800.00 (Rating: 5, Released: 1/10/2023)
Laptop - $1200.00 (Rating: 4, Released: 5/15/2022)

Best value sorting:
Headphones - $150.00 (Rating: 5, Released: 8/20/2022)
Smartphone - $800.00 (Rating: 5, Released: 1/10/2023)
Laptop - $1200.00 (Rating: 4, Released: 5/15/2022)
Smartwatch - $250.00 (Rating: 4, Released: 2/14/2023)
Tablet - $400.00 (Rating: 3, Released: 11/5/2021)

Newest products first:
Smartwatch - $250.00 (Rating: 4, Released: 2/14/2023)
Smartphone - $800.00 (Rating: 5, Released: 1/10/2023)
Headphones - $150.00 (Rating: 5, Released: 8/20/2022)
Laptop - $1200.00 (Rating: 4, Released: 5/15/2022)
Tablet - $400.00 (Rating: 3, Released: 11/5/2021)

This demonstrates how LINQ's ordering capabilities can help implement different sorting options for users in a real application.

Performance Considerations

When working with LINQ ordering, keep these performance considerations in mind:

  1. Deferred Execution: Ordering operations are not executed until the sequence is enumerated, which can help with performance.
  2. Memory Usage: Ordering requires loading the entire collection into memory, so be cautious with very large datasets.
  3. Multiple Ordering: Each chained ordering operation creates another pass through the data, so try to minimize the number of sorting levels.
  4. Stable Sorting: LINQ's ordering is stable, meaning that equal elements maintain their original order.

Summary

LINQ's ordering operators provide a flexible and expressive way to sort data collections in .NET applications. The key takeaways from this lesson are:

  • Use OrderBy and OrderByDescending for basic sorting
  • Use ThenBy and ThenByDescending for multi-level sorting
  • You can sort by any property or computed value
  • Custom comparers offer advanced sorting capabilities
  • Ordering is deferred until the sequence is enumerated

Whether you're sorting simple numeric collections or implementing complex sorting logic for business objects, LINQ's ordering capabilities help you write clean, declarative code.

Practice Exercises

  1. Create a Book class with properties for Title, Author, and PublishedYear. Sort a collection of books by author, and then by published year.

  2. Create a collection of Employee objects with Name, Department, and Salary properties. Write LINQ queries to:

    • Sort employees by department, then by salary (highest first)
    • Find the highest-paid employee in each department
    • Sort employees alphabetically by name
  3. Using the earlier Product example, implement a flexible sorting function that takes a sort option parameter (e.g., "price", "rating", "date") and returns the collection sorted accordingly.

Additional Resources

Happy sorting with LINQ!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)