C# LINQ
Querying data with powerful, readable syntax
🔍 What is LINQ?
LINQ (Language Integrated Query) lets you query collections using SQL-like syntax directly in C#. It makes filtering, sorting, and transforming data simple and readable with powerful built-in methods.
using System.Linq;
// Simple LINQ query
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (int num in evenNumbers)
Console.WriteLine(num); // 2, 4, 6
Output:
2
4
6
Common LINQ Operations
Where
Filter items by condition
numbers.Where(n =>
n > 5)
Select
Transform each item
names.Select(n =>
n.ToUpper())
OrderBy
Sort items in order
items.OrderBy(i =>
i.Price)
First/Any
Find specific items
list.First(x =>
x.Id == 5)
🔹 Filtering with Where
The LINQ Where method filters collections based on a specified predicate, returning only elements that satisfy the condition. Applied to a list of numbers, Where(n => n % 2 == 0) extracts even numbers: 2, 4, 6, 8, 10. You can chain multiple conditions: Where(n => n > 5) yields 6, 7, 8, 9, 10. For range filtering, Where(n => n > 3 && n < 8) gives 4, 5, 6, 7. This provides a declarative, readable alternative to manual foreach loops with if statements for data selection tasks.
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Get even numbers
var evenNumbers = numbers.Where(n => n % 2 == 0);
Console.WriteLine("Even numbers:");
foreach (int num in evenNumbers)
{
Console.Write(num + " ");
}
// Get numbers greater than 5
var largeNumbers = numbers.Where(n => n > 5);
Console.WriteLine("\n\nNumbers > 5:");
foreach (int num in largeNumbers)
{
Console.Write(num + " ");
}
// Multiple conditions
var filtered = numbers.Where(n => n > 3 && n < 8);
Console.WriteLine("\n\nNumbers between 3 and 8:");
foreach (int num in filtered)
{
Console.Write(num + " ");
}
}
}
Output:
Even numbers:
2 4 6 8 10
Numbers > 5:
6 7 8 9 10
Numbers between 3 and 8:
4 5 6 7
🔹 Transforming with Select
The Select method transforms each item in a collection into a new form using a lambda expression. It is a projection operator that returns an IEnumerable of the transformed results without altering the original collection. This is ideal for extracting specific properties, performing calculations, or formatting data. For example, names.Select(n => n.Length) converts a list of strings into a sequence of their respective character counts, enabling efficient data reshaping.
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> names = new List<string> { "alice", "bob", "charlie" };
// Convert to uppercase
var upperNames = names.Select(n => n.ToUpper());
Console.WriteLine("Uppercase names:");
foreach (string name in upperNames)
{
Console.WriteLine(name);
}
// Get string lengths
var lengths = names.Select(n => n.Length);
Console.WriteLine("\nName lengths:");
foreach (int length in lengths)
{
Console.Write(length + " ");
}
// Create new objects
var nameObjects = names.Select(n => new {
Original = n,
Upper = n.ToUpper(),
Length = n.Length
});
Console.WriteLine("\n\nName details:");
foreach (var obj in nameObjects)
{
Console.WriteLine($"{obj.Original} -> {obj.Upper} ({obj.Length} chars)");
}
}
}
Output:
Uppercase names:
ALICE
BOB
CHARLIE
Name lengths:
5 3 7
Name details:
alice -> ALICE (5 chars)
bob -> BOB (3 chars)
charlie -> CHARLIE (7 chars)
🔹 Sorting with OrderBy
OrderBy arranges elements in ascending order based on a key, while OrderByDescending sorts in reverse. Both methods accept a key selector lambda, such as OrderBy(p => p.Age), and support chaining with ThenBy for secondary sorts. Sorting is deferred until iteration, preserving performance. This is essential for organizing data for display, reporting, or further processing, ensuring logical and user-friendly sequences in applications.
using System;
using System.Linq;
using System.Collections.Generic;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 }
};
// Sort by name
var byName = people.OrderBy(p => p.Name);
Console.WriteLine("Sorted by name:");
foreach (var person in byName)
{
Console.WriteLine($"{person.Name} ({person.Age})");
}
// Sort by age (descending)
var byAge = people.OrderByDescending(p => p.Age);
Console.WriteLine("\nSorted by age (descending):");
foreach (var person in byAge)
{
Console.WriteLine($"{person.Name} ({person.Age})");
}
// Sort numbers
int[] numbers = { 5, 2, 8, 1, 9, 3 };
var sorted = numbers.OrderBy(n => n);
Console.WriteLine("\nSorted numbers: " + string.Join(", ", sorted));
}
}
Output:
Sorted by name:
Alice (25)
Bob (30)
Charlie (35)
Sorted by age (descending):
Charlie (35)
Bob (30)
Alice (25)
Sorted numbers: 1, 2, 3, 5, 8, 9
🔹 Finding Items with First, Any, and Count
LINQ provides efficient element retrieval and condition-checking methods like First, Any, and Count. First returns the initial element matching a condition, throwing if none exist. Any checks for at least one match, and Count returns the number of matches. These methods eliminate manual loops, enhancing code readability and performance for queries such as validating data existence or aggregating filtered results.
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Get first item
int first = numbers.First();
Console.WriteLine("First number: " + first);
// Get first item matching condition
int firstEven = numbers.First(n => n % 2 == 0);
Console.WriteLine("First even number: " + firstEven);
// Check if any items match
bool hasLarge = numbers.Any(n => n > 5);
Console.WriteLine("Has numbers > 5? " + hasLarge);
// Count items
int evenCount = numbers.Count(n => n % 2 == 0);
Console.WriteLine("Count of even numbers: " + evenCount);
// Get last item
int last = numbers.Last();
Console.WriteLine("Last number: " + last);
// Check if all items match
bool allPositive = numbers.All(n => n > 0);
Console.WriteLine("All positive? " + allPositive);
}
}
Output:
First number: 1
First even number: 2
Has numbers > 5? True
Count of even numbers: 5
Last number: 10
All positive? True
🔹 Chaining LINQ Methods
Method chaining combines multiple LINQ operations into a single, fluent query for complex data processing. Each method returns a new IEnumerable, allowing sequential filtering, sorting, and projecting. For example, products.Where(p => p.Price < 500).OrderBy(p => p.Price).Select(p => p.Name) retrieves affordable items sorted by price. This approach improves code conciseness, maintainability, and performance by processing data in a streamlined pipeline.
using System;
using System.Linq;
using System.Collections.Generic;
class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
class Program
{
static void Main()
{
List<Product> products = new List<Product>
{
new Product { Name = "Laptop", Price = 999, Category = "Electronics" },
new Product { Name = "Mouse", Price = 25, Category = "Electronics" },
new Product { Name = "Desk", Price = 300, Category = "Furniture" },
new Product { Name = "Chair", Price = 150, Category = "Furniture" },
new Product { Name = "Monitor", Price = 400, Category = "Electronics" }
};
// Chain multiple operations
var result = products
.Where(p => p.Category == "Electronics") // Filter
.Where(p => p.Price > 100) // More filtering
.OrderBy(p => p.Price) // Sort
.Select(p => p.Name); // Transform
Console.WriteLine("Affordable electronics (sorted by price):");
foreach (string name in result)
{
Console.WriteLine("- " + name);
}
// Calculate average price
var avgPrice = products
.Where(p => p.Category == "Electronics")
.Average(p => p.Price);
Console.WriteLine($"\nAverage electronics price: ${avgPrice:F2}");
}
}
Output:
Affordable electronics (sorted by price):
- Monitor
- Laptop
Average electronics price: $474.67
🔹 Query Syntax vs Method Syntax
LINQ offers two interchangeable styles: query syntax (SQL-like) and method syntax (lambda-based). Query syntax uses keywords like from, where, and select for readable, declarative queries. Method syntax employs extension methods and lambdas, offering greater flexibility and integration with C# code. Both compile to identical IL, so the choice depends on developer preference and query complexity for optimal clarity and maintainability.
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Query Syntax (SQL-like)
var queryResult = from n in numbers
where n % 2 == 0
orderby n descending
select n;
Console.WriteLine("Query syntax result:");
Console.WriteLine(string.Join(", ", queryResult));
// Method Syntax (Lambda expressions)
var methodResult = numbers
.Where(n => n % 2 == 0)
.OrderByDescending(n => n);
Console.WriteLine("\nMethod syntax result:");
Console.WriteLine(string.Join(", ", methodResult));
Console.WriteLine("\nBoth produce the same output!");
}
}
Output:
Query syntax result:
10, 8, 6, 4, 2
Method syntax result:
10, 8, 6, 4, 2
Both produce the same output!