Skip to main content

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 order
  • OrderByDescending: Sorts elements in descending order
  • ThenBy: Performs secondary sorting in ascending order
  • ThenByDescending: 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

csharp
var sortedCollection = collection.OrderBy(item => item.Property);

Example: Sorting Numbers

csharp
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

csharp
var sortedCollection = collection.OrderByDescending(item => item.Property);

Example: Sorting Strings in Descending Order

csharp
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

csharp
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

csharp
var sortedCollection = collection
.OrderBy(item => item.PrimaryProperty)
.ThenBy(item => item.SecondaryProperty);

Example: Sorting Students by GPA and then by Name

csharp
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

csharp
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:

csharp
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:

  1. Deferred Execution: LINQ sorting operations use deferred execution, meaning they aren't performed until the results are actually enumerated.

  2. Multiple Enumerations: Be careful about enumerating sorted results multiple times. Consider using .ToList() or .ToArray() to cache results if needed.

  3. Memory Usage: Sorting requires keeping all items in memory. For very large collections, consider other approaches.

csharp
// 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 and ThenByDescending 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

  1. Create a list of your favorite movies with properties like Title, Year, and Rating. Sort them in different ways using LINQ.

  2. Given a list of employees with Name, Department, and Salary properties, sort them first by department and then by salary (highest to lowest).

  3. Create a custom comparer that sorts strings by length and then alphabetically.

  4. 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! :)