.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 orderOrderByDescending
- Orders elements in descending orderThenBy
- Performs a secondary sort in ascending orderThenByDescending
- 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
.
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
:
// 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:
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:
// 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:
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:
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:
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:
- Deferred Execution: Ordering operations are not executed until the sequence is enumerated, which can help with performance.
- Memory Usage: Ordering requires loading the entire collection into memory, so be cautious with very large datasets.
- Multiple Ordering: Each chained ordering operation creates another pass through the data, so try to minimize the number of sorting levels.
- 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
andOrderByDescending
for basic sorting - Use
ThenBy
andThenByDescending
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
-
Create a
Book
class with properties forTitle
,Author
, andPublishedYear
. Sort a collection of books by author, and then by published year. -
Create a collection of
Employee
objects withName
,Department
, andSalary
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
-
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
- Microsoft Documentation: OrderBy Method
- Microsoft Documentation: ThenBy Method
- C# LINQ Fundamentals Course on Pluralsight
- 101 LINQ Samples
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! :)