C# LINQ Filtering
LINQ (Language Integrated Query) offers powerful ways to filter collections in C#, allowing you to extract exactly the data you need with minimal code. In this tutorial, we'll explore how to use LINQ's filtering capabilities to transform your data manipulation code.
Introduction to LINQ Filtering
Filtering is one of the most common operations in data processing. It allows you to select only the elements from a collection that meet specific criteria. Before LINQ, you would typically use loops and conditional statements to filter data. LINQ simplifies this with expressive, readable syntax.
Basic Filtering with Where
The Where
method is the primary filtering operator in LINQ. It accepts a predicate (a function that returns a boolean value) and returns elements that satisfy the condition.
Syntax
var result = collection.Where(item => condition);
Example: Filtering Numbers
Let's start with a simple example of filtering even numbers:
using System;
using System.Linq;
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Filter even numbers
var evenNumbers = numbers.Where(n => n % 2 == 0);
Console.WriteLine("Even numbers:");
foreach (var number in evenNumbers)
{
Console.WriteLine(number);
}
Output:
Even numbers:
2
4
6
8
10
Example: Filtering Objects
Filtering works with complex objects too. Let's filter a list of students:
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public double GPA { get; set; }
}
// Creating a list of students
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Age = 22, GPA = 3.8 },
new Student { Name = "Bob", Age = 19, GPA = 2.9 },
new Student { Name = "Charlie", Age = 21, GPA = 3.5 },
new Student { Name = "Diana", Age = 20, GPA = 3.9 },
new Student { Name = "Evan", Age = 23, GPA = 2.7 }
};
// Filter students with GPA above 3.5
var highPerformers = students.Where(s => s.GPA > 3.5);
Console.WriteLine("High performing students:");
foreach (var student in highPerformers)
{
Console.WriteLine($"{student.Name}: {student.GPA} GPA");
}
Output:
High performing students:
Alice: 3.8 GPA
Diana: 3.9 GPA
Multiple Conditions
You can combine multiple conditions using logical operators:
// Students who are 21 or older AND have GPA of 3.0 or higher
var qualifiedStudents = students.Where(s => s.Age >= 21 && s.GPA >= 3.0);
Console.WriteLine("Qualified students (21+ with 3.0+ GPA):");
foreach (var student in qualifiedStudents)
{
Console.WriteLine($"{student.Name}: Age {student.Age}, GPA {student.GPA}");
}
Output:
Qualified students (21+ with 3.0+ GPA):
Alice: Age 22, GPA 3.8
Charlie: Age 21, GPA 3.5
Filtering with Index Using Where
LINQ's Where method has an overload that provides the index of each element:
// Get every other student starting from the first one
var alternateStudents = students.Where((s, index) => index % 2 == 0);
Console.WriteLine("Students at even indices (0, 2, 4...):");
foreach (var student in alternateStudents)
{
Console.WriteLine(student.Name);
}
Output:
Students at even indices (0, 2, 4...):
Alice
Charlie
Evan
Other Filtering Methods
OfType<T>
The OfType<T>
method filters elements based on their ability to be cast to a specified type:
// Mixed collection
var items = new ArrayList { "hello", 123, true, "world", 456 };
// Filter only strings
var onlyStrings = items.OfType<string>();
Console.WriteLine("Strings from mixed collection:");
foreach (var item in onlyStrings)
{
Console.WriteLine(item);
}
Output:
Strings from mixed collection:
hello
world
Distinct
The Distinct
method removes duplicate elements from a collection:
List<int> numbersWithDuplicates = new List<int> { 1, 2, 2, 3, 4, 4, 5, 6, 6, 7 };
// Get only distinct numbers
var uniqueNumbers = numbersWithDuplicates.Distinct();
Console.WriteLine("Distinct numbers:");
foreach (var number in uniqueNumbers)
{
Console.WriteLine(number);
}
Output:
Distinct numbers:
1
2
3
4
5
6
7
Take and Skip
Take
and Skip
methods allow you to filter collections based on position:
List<string> fruits = new List<string>
{
"Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"
};
// Take first 3 elements
var firstThreeFruits = fruits.Take(3);
Console.WriteLine("First three fruits:");
foreach (var fruit in firstThreeFruits)
{
Console.WriteLine(fruit);
}
// Skip 4 elements and take the rest
var lastFruits = fruits.Skip(4);
Console.WriteLine("\nLast fruits (skipped first 4):");
foreach (var fruit in lastFruits)
{
Console.WriteLine(fruit);
}
Output:
First three fruits:
Apple
Banana
Cherry
Last fruits (skipped first 4):
Elderberry
Fig
Grape
Real-World Example: Filtering Product Inventory
Let's see a more realistic example of how LINQ filtering might be used in a product inventory system:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public string Category { get; set; }
public int StockQuantity { get; set; }
public DateTime ExpiryDate { get; set; }
}
// Create sample inventory
List<Product> inventory = new List<Product>
{
new Product { Id = 1, Name = "Organic Milk", Price = 3.99, Category = "Dairy", StockQuantity = 45, ExpiryDate = DateTime.Now.AddDays(7) },
new Product { Id = 2, Name = "Whole Grain Bread", Price = 2.49, Category = "Bakery", StockQuantity = 12, ExpiryDate = DateTime.Now.AddDays(4) },
new Product { Id = 3, Name = "Cheddar Cheese", Price = 4.50, Category = "Dairy", StockQuantity = 32, ExpiryDate = DateTime.Now.AddDays(14) },
new Product { Id = 4, Name = "Apples", Price = 1.99, Category = "Produce", StockQuantity = 90, ExpiryDate = DateTime.Now.AddDays(10) },
new Product { Id = 5, Name = "Chicken Breast", Price = 5.99, Category = "Meat", StockQuantity = 8, ExpiryDate = DateTime.Now.AddDays(3) },
new Product { Id = 6, Name = "Chocolate Cookies", Price = 3.29, Category = "Bakery", StockQuantity = 25, ExpiryDate = DateTime.Now.AddDays(21) },
new Product { Id = 7, Name = "Greek Yogurt", Price = 1.49, Category = "Dairy", StockQuantity = 18, ExpiryDate = DateTime.Now.AddDays(9) },
};
// Find dairy products with stock below 20
var dairyLowStock = inventory
.Where(p => p.Category == "Dairy" && p.StockQuantity < 20);
Console.WriteLine("Dairy products with low stock (<20):");
foreach (var product in dairyLowStock)
{
Console.WriteLine($"{product.Name}: {product.StockQuantity} in stock");
}
// Find products expiring within 5 days
var expiringProducts = inventory
.Where(p => (p.ExpiryDate - DateTime.Now).TotalDays <= 5)
.OrderBy(p => p.ExpiryDate);
Console.WriteLine("\nProducts expiring within 5 days:");
foreach (var product in expiringProducts)
{
int daysLeft = (int)(product.ExpiryDate - DateTime.Now).TotalDays;
Console.WriteLine($"{product.Name}: Expires in {daysLeft} days");
}
// Find products under $3 with good stock levels
var budgetDeals = inventory
.Where(p => p.Price < 3.00 && p.StockQuantity > 10)
.OrderBy(p => p.Price);
Console.WriteLine("\nBudget-friendly products (under $3 with 10+ in stock):");
foreach (var product in budgetDeals)
{
Console.WriteLine($"{product.Name}: ${product.Price} - {product.StockQuantity} available");
}
Output:
Dairy products with low stock (<20):
Greek Yogurt: 18 in stock
Products expiring within 5 days:
Chicken Breast: Expires in 3 days
Whole Grain Bread: Expires in 4 days
Budget-friendly products (under $3 with 10+ in stock):
Greek Yogurt: $1.49 - 18 available
Apples: $1.99 - 90 available
Whole Grain Bread: $2.49 - 12 available
Chaining Filters
You can chain multiple LINQ methods to create sophisticated queries:
// Find products that are either dairy items with price < $2 OR bakery items with stock > 20
var specialProducts = inventory
.Where(p => (p.Category == "Dairy" && p.Price < 2.00) ||
(p.Category == "Bakery" && p.StockQuantity > 20))
.OrderBy(p => p.Category)
.ThenBy(p => p.Name);
Console.WriteLine("Special products (cheap dairy OR well-stocked bakery):");
foreach (var product in specialProducts)
{
Console.WriteLine($"{product.Category} - {product.Name}: ${product.Price}, {product.StockQuantity} in stock");
}
Output:
Special products (cheap dairy OR well-stocked bakery):
Bakery - Chocolate Cookies: $3.29, 25 in stock
Dairy - Greek Yogurt: $1.49, 18 in stock
Summary
LINQ filtering provides elegant ways to extract and process data from collections in C#. Through the Where
method and related operators, you can create expressive, readable queries that replace more verbose traditional approaches.
Key points to remember:
- The
Where
method is the main filtering operator - You can combine multiple conditions with logical operators
- Other filtering methods like
OfType<T>
,Distinct
,Take
, andSkip
serve specific filtering needs - Filtering can be combined with other LINQ operations through method chaining
Exercises
To practice your LINQ filtering skills, try these exercises:
-
Create a list of 10 random numbers and filter out:
- Numbers greater than 50
- Even numbers less than 30
- Numbers divisible by 3 or 5
-
Using the
Product
class from the examples:- Find products that expire in more than 10 days but are low on stock (< 15)
- Find the 3 most expensive products in each category
- Find products where the name contains a specific letter and the price is within a certain range
-
Create a collection of strings and:
- Filter out strings that contain a specific character
- Filter out strings with fewer than 5 characters
- Find strings that start with a vowel
Further Reading
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)