Skip to main content

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

csharp
var result = collection.Where(item => condition);

Example: Filtering Numbers

Let's start with a simple example of filtering even numbers:

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

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

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

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

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

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

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

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

csharp
// 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, and Skip 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:

  1. 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
  2. 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
  3. 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! :)