C# LINQ Sorting
When working with collections in C#, sorting data is a common requirement. LINQ (Language Integrated Query) provides powerful and flexible methods to sort data in different ways. In this tutorial, we'll explore how to use LINQ's sorting operations to organize your data efficiently.
Introduction to LINQ Sorting
LINQ offers several methods for sorting collections:
OrderBy
: Sorts elements in ascending orderOrderByDescending
: Sorts elements in descending orderThenBy
: Performs secondary sorting in ascending orderThenByDescending
: Performs secondary sorting in descending order
These methods allow you to sort collections based on one or more properties with minimal code.
Basic Sorting with OrderBy
The OrderBy
method sorts a collection in ascending order based on a key that you specify.
Syntax
var sortedCollection = collection.OrderBy(item => item.Property);
Example: Sorting Numbers
using System;
using System.Linq;
class Program
{
static void Main()
{
// Creating a collection of integers
int[] numbers = { 5, 2, 9, 1, 7, 3 };
// Sorting in ascending order
var sortedNumbers = numbers.OrderBy(n => n);
Console.WriteLine("Original numbers:");
foreach (var num in numbers)
{
Console.Write($"{num} ");
}
Console.WriteLine("\n\nSorted numbers (ascending):");
foreach (var num in sortedNumbers)
{
Console.Write($"{num} ");
}
}
}
Output:
Original numbers:
5 2 9 1 7 3
Sorted numbers (ascending):
1 2 3 5 7 9
Sorting in Descending Order
Use the OrderByDescending
method to sort elements in descending order.
Syntax
var sortedCollection = collection.OrderByDescending(item => item.Property);
Example: Sorting Strings in Descending Order
using System;
using System.Linq;
class Program
{
static void Main()
{
string[] fruits = { "apple", "banana", "cherry", "date", "elderberry" };
// Sorting in descending order
var descendingFruits = fruits.OrderByDescending(fruit => fruit);
Console.WriteLine("Fruits in descending order:");
foreach (var fruit in descendingFruits)
{
Console.WriteLine(fruit);
}
}
}
Output:
Fruits in descending order:
elderberry
date
cherry
banana
apple
Sorting Complex Objects
When working with collections of objects, you can sort based on any property.
Example: Sorting Students by Age
using System;
using System.Collections.Generic;
using System.Linq;
class Student
{
public string Name { get; set; }
public int Age { get; set; }
public double GPA { get; set; }
public override string ToString()
{
return $"{Name}, Age: {Age}, GPA: {GPA}";
}
}
class Program
{
static void Main()
{
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Age = 22, GPA = 3.8 },
new Student { Name = "Bob", Age = 19, GPA = 3.5 },
new Student { Name = "Charlie", Age = 21, GPA = 3.9 },
new Student { Name = "Diana", Age = 20, GPA = 4.0 }
};
// Sort students by age
var sortedByAge = students.OrderBy(s => s.Age);
Console.WriteLine("Students sorted by age (ascending):");
foreach (var student in sortedByAge)
{
Console.WriteLine(student);
}
}
}
Output:
Students sorted by age (ascending):
Bob, Age: 19, GPA: 3.5
Diana, Age: 20, GPA: 4.0
Charlie, Age: 21, GPA: 3.9
Alice, Age: 22, GPA: 3.8
Multiple-Level Sorting with ThenBy and ThenByDescending
Sometimes you need to sort by multiple criteria. For example, sorting students first by grade and then by name. This is where ThenBy
and ThenByDescending
come in.
Syntax
var sortedCollection = collection
.OrderBy(item => item.PrimaryProperty)
.ThenBy(item => item.SecondaryProperty);
Example: Sorting Students by GPA and then by Name
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Age = 22, GPA = 3.8 },
new Student { Name = "Bob", Age = 19, GPA = 3.5 },
new Student { Name = "Charlie", Age = 21, GPA = 3.9 },
new Student { Name = "Diana", Age = 20, GPA = 3.5 }
};
// Sort students by GPA (descending) and then by Name (ascending)
var sortedStudents = students
.OrderByDescending(s => s.GPA)
.ThenBy(s => s.Name);
Console.WriteLine("Students sorted by GPA (descending) and then by Name:");
foreach (var student in sortedStudents)
{
Console.WriteLine(student);
}
}
}
Output:
Students sorted by GPA (descending) and then by Name:
Diana, Age: 20, GPA: 4.0
Charlie, Age: 21, GPA: 3.9
Alice, Age: 22, GPA: 3.8
Bob, Age: 19, GPA: 3.5
Diana, Age: 20, GPA: 3.5
Custom Sorting with IComparer
For more complex sorting scenarios, you can create custom comparers by implementing the IComparer<T>
interface.
Example: Custom Sorting of Students
using System;
using System.Collections.Generic;
using System.Linq;
class StudentNameComparer : IComparer<Student>
{
public int Compare(Student x, Student y)
{
// Case-insensitive name comparison
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
class Program
{
static void Main()
{
List<Student> students = new List<Student>
{
new Student { Name = "alice", Age = 22, GPA = 3.8 },
new Student { Name = "Bob", Age = 19, GPA = 3.5 },
new Student { Name = "Charlie", Age = 21, GPA = 3.9 }
};
// Using custom comparer
var customSorted = students.OrderBy(s => s, new StudentNameComparer());
Console.WriteLine("Students sorted using custom comparer (case-insensitive name):");
foreach (var student in customSorted)
{
Console.WriteLine(student);
}
}
}
Real-World Example: Product Catalog Sorting
Let's implement a more practical example of sorting a product catalog:
using System;
using System.Collections.Generic;
using System.Linq;
class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
public int StockQuantity { get; set; }
public override string ToString()
{
return $"{Name} - ${Price} ({Category}) - {StockQuantity} in stock";
}
}
class ProductCatalog
{
private List<Product> products;
public ProductCatalog()
{
products = new List<Product>
{
new Product { Name = "Laptop", Price = 999.99m, Category = "Electronics", StockQuantity = 12 },
new Product { Name = "Smartphone", Price = 499.99m, Category = "Electronics", StockQuantity = 25 },
new Product { Name = "Headphones", Price = 89.99m, Category = "Electronics", StockQuantity = 35 },
new Product { Name = "Desk Chair", Price = 199.99m, Category = "Furniture", StockQuantity = 8 },
new Product { Name = "Coffee Table", Price = 149.99m, Category = "Furniture", StockQuantity = 5 },
new Product { Name = "Book Shelf", Price = 89.99m, Category = "Furniture", StockQuantity = 15 }
};
}
public void DisplayProducts(IEnumerable<Product> productList)
{
foreach (var product in productList)
{
Console.WriteLine(product);
}
Console.WriteLine();
}
public void SortProductsByPrice()
{
var sortedByPrice = products.OrderBy(p => p.Price);
Console.WriteLine("Products sorted by price (lowest first):");
DisplayProducts(sortedByPrice);
}
public void SortProductsByCategory()
{
var sortedByCategory = products.OrderBy(p => p.Category).ThenBy(p => p.Name);
Console.WriteLine("Products sorted by category and then by name:");
DisplayProducts(sortedByCategory);
}
public void SortProductsCustom()
{
// Products with low stock first, then by price
var customSorted = products.OrderBy(p => p.StockQuantity <= 10 ? 0 : 1)
.ThenBy(p => p.Price);
Console.WriteLine("Products with low stock first, then by price:");
DisplayProducts(customSorted);
}
}
class Program
{
static void Main()
{
ProductCatalog catalog = new ProductCatalog();
catalog.SortProductsByPrice();
catalog.SortProductsByCategory();
catalog.SortProductsCustom();
}
}
This example demonstrates how to sort products in an e-commerce catalog using different criteria that would be helpful in a real application.
Performance Considerations
When working with LINQ sorting operations, keep these performance aspects in mind:
-
Deferred Execution: LINQ sorting operations use deferred execution, meaning they aren't performed until the results are actually enumerated.
-
Multiple Enumerations: Be careful about enumerating sorted results multiple times. Consider using
.ToList()
or.ToArray()
to cache results if needed. -
Memory Usage: Sorting requires keeping all items in memory. For very large collections, consider other approaches.
// Without caching (will sort twice)
var sorted = collection.OrderBy(x => x);
foreach (var item in sorted) { /* use item */ }
foreach (var item in sorted) { /* use item again - this will re-sort the collection */ }
// With caching (sorts once)
var sortedCached = collection.OrderBy(x => x).ToList();
foreach (var item in sortedCached) { /* use item */ }
foreach (var item in sortedCached) { /* use item again - no re-sorting */ }
Summary
LINQ sorting methods provide a powerful and flexible way to sort collections in C#:
- Use
OrderBy
for basic ascending sorting - Use
OrderByDescending
for basic descending sorting - Use
ThenBy
andThenByDescending
for multi-level sorting - Implement custom comparers for specialized sorting logic
These methods allow you to write clear, concise code for sorting any type of collection.
Exercises
-
Create a list of your favorite movies with properties like Title, Year, and Rating. Sort them in different ways using LINQ.
-
Given a list of employees with Name, Department, and Salary properties, sort them first by department and then by salary (highest to lowest).
-
Create a custom comparer that sorts strings by length and then alphabetically.
-
Sort a list of dates to find the closest dates to the current date.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)