ZetCode

C# LINQ Where

last modified November 6, 2026

In this article we show how to filter sequences in C# using LINQ's Where method and the where clause.

Language-Integrated Query (LINQ) is a domain-specific language for querying data from various data sources, including arrays, lists, XML files, or databases.

The where clause or the Where method filters a sequence of values based on a predicate. A predicate is a single-argument function which returns a boolean value.

This method is lazily evaluated. The query is not executed until the object is enumerated.

C# LINQ Where simple example

The first example is a simple program which uses LINQ Where.

Program.cs
List<int> vals = [-3, -1, 0, 3, 2, 1, -6, 5, 9];
var res = vals.Where(e => e > 0);

Console.WriteLine(string.Join(',', res));

The program defines a list of integers. We keep only the positive values.

var res = vals.Where(e => e > 0);

The Where method applies the given predicate function on each of the elements. It returns a sequence of values that satisfy the given condition. In our case the condition is that the value is greater than zero.

$ dotnet run
3,2,1,5,9

C# LINQ Where with Func predicate

Func is a built-in generic delegate type. It can refer to a method, an anonymous method, or a lambda expression.

Program.cs
Func<int, bool> isNeg = e => e < 0;

List<int> vals = [-3, -1, 0, 3, 2, 1, -6, 5, 9];

var res = vals.Where(isNeg);
Console.WriteLine(string.Join(',', res));

var res2 = vals.Where(IsEven);
Console.WriteLine(string.Join(',', res2));

static bool IsEven(int e) => e % 2 == 0;

The program applies the Func delegate to the elements of the list. We select all negative values.

var res2 = vals.Where(IsEven);

We can also pass a method group. The IsEven local function matches the Func<int, bool> signature.

$ dotnet run
-3,-1,-6
0,2,-6

C# LINQ where clause

Query expressions are an alternative LINQ syntax. In a query expression, we use the where clause to filter data.

Program.cs
List<string> words = [ "sky", "forest", "war", "buy",
    "crypto", "cup", "water", "cloud" ];

var res = from word in words
          where word.StartsWith('c')
          select word;

Console.WriteLine(string.Join(',', res));

var res2 = from word in words
           where word.Length == 3
           where word.Contains('u')
           select word.ToUpper();

Console.WriteLine(string.Join(',', res2));

In the program, we select all words that begin with letter 'c'.

var res2 = from word in words
           where word.Length == 3
           where word.Contains('u')
           select word.ToUpper();

A query may contain several where clauses; an element must satisfy all of them. The select clause transforms the filtered words to uppercase.

$ dotnet run
crypto,cup,cloud
BUY,CUP

C# LINQ Where with multiple conditions

We can combine multiple conditions with the && and || operators.

Program.cs
List<User> users =
[
    new("John", "Doe", 1230),
    new("Lucy", "Novak", 670),
    new("Ben", "Walter", 2050),
    new("Robin", "Brown", 2300),
    new("Amy", "Doe", 1250),
    new("Joe", "Draker", 1190),
    new("Janet", "Doe", 980),
    new("Albert", "Novak", 1930),
];

var res = users.Where(u => (u.LastName.StartsWith('D') ||
    u.LastName.StartsWith('W')) && u.Salary > 1000);

foreach (var e in res)
{
    Console.WriteLine(e);
}

Console.WriteLine("----------------------");

var res2 = users.Where(u => u is { LastName: "Novak" or "Brown", Salary: > 1500 });

foreach (var e in res2)
{
    Console.WriteLine(e);
}

record User(string FirstName, string LastName, int Salary);

We apply three conditions to our filtering. We choose only those users whose last names begin with either 'D' or 'W' and whose salaries are higher than 1000.

var res = users.Where(u => (u.LastName.StartsWith('D') ||
    u.LastName.StartsWith('W')) && u.Salary > 1000);

The parentheses are needed because && has a higher precedence than ||.

var res2 = users.Where(u => u is { LastName: "Novak" or "Brown", Salary: > 1500 });

Conditions can also be expressed with a property pattern. We select Novaks and Browns with salaries over 1500.

$ dotnet run
User { FirstName = John, LastName = Doe, Salary = 1230 }
User { FirstName = Ben, LastName = Walter, Salary = 2050 }
User { FirstName = Amy, LastName = Doe, Salary = 1250 }
User { FirstName = Joe, LastName = Draker, Salary = 1190 }
----------------------
User { FirstName = Robin, LastName = Brown, Salary = 2300 }
User { FirstName = Albert, LastName = Novak, Salary = 1930 }

C# LINQ Where with index

The Where method has an overload whose predicate receives the zero-based index of the element as the second parameter.

Program.cs
List<string> words = [ "sky", "forest", "war", "buy",
    "crypto", "cup", "water", "cloud" ];

var res = words.Where((word, idx) => idx % 2 == 0);
Console.WriteLine(string.Join(',', res));

var res2 = words.Where((word, idx) => word.Length > idx);
Console.WriteLine(string.Join(',', res2));

We filter words by their positions in the list.

var res = words.Where((word, idx) => idx % 2 == 0);

We select the words at even indexes.

var res2 = words.Where((word, idx) => word.Length > idx);

Here we keep the words that are longer than their index.

$ dotnet run
sky,war,crypto,water
sky,forest,war,crypto

C# LINQ Where deferred execution

The Where method does not filter anything when it is called. It returns an object that stores the predicate and the source. The elements are tested only when the result is enumerated.

Program.cs
List<int> vals = [1, 2, 3, 4, 5];

var res = vals.Where(e =>
{
    Console.WriteLine($"checking {e}");
    return e % 2 == 0;
});

Console.WriteLine("query defined");

vals.Add(6);

foreach (var e in res)
{
    Console.WriteLine($"got {e}");
}

var snapshot = vals.Where(e => e > 3).ToList();
vals.Add(7);

Console.WriteLine(string.Join(',', snapshot));

The predicate prints each element it checks.

Console.WriteLine("query defined");

vals.Add(6);

No element is checked when the query is defined. The element added afterwards is included, since the query reads the list during enumeration.

var snapshot = vals.Where(e => e > 3).ToList();
vals.Add(7);

The ToList method executes the query immediately and stores the results. Later changes to the source do not affect the snapshot.

$ dotnet run
query defined
checking 1
checking 2
got 2
checking 3
checking 4
got 4
checking 5
checking 6
got 6
4,5,6

C# LINQ Where with Select

Where is often chained with other LINQ methods. Methods such as Count and Any also accept a predicate directly.

Program.cs
List<Product> products =
[
    new("laptop", "electronics", 1200m, true),
    new("mouse", "electronics", 25m, false),
    new("desk", "furniture", 340m, true),
    new("chair", "furniture", 150m, true),
    new("monitor", "electronics", 280m, true),
    new("lamp", "furniture", 45m, false),
];

var res = products
    .Where(p => p.InStock)
    .Where(p => p.Category == "electronics")
    .Select(p => $"{p.Name}: {p.Price:F2}");

Console.WriteLine(string.Join(Environment.NewLine, res));

Console.WriteLine(products.Count(p => p.Price > 200m));
Console.WriteLine(products.Any(p => !p.InStock));

record Product(string Name, string Category, decimal Price, bool InStock);

We have a list of products.

var res = products
    .Where(p => p.InStock)
    .Where(p => p.Category == "electronics")
    .Select(p => $"{p.Name}: {p.Price:F2}");

We select electronic products that are in stock and project them to strings. The elements flow through the chain one at a time.

Console.WriteLine(products.Count(p => p.Price > 200m));
Console.WriteLine(products.Any(p => !p.InStock));

Instead of Where(...).Count(), we can pass the predicate to Count. The Any method stops at the first matching element.

$ dotnet run
laptop: 1200.00
monitor: 280.00
3
True

C# filter nulls and types

A common task is to remove null or empty values, or to keep only elements of a certain type.

Program.cs
List<string?> names = ["Peter", null, "Lucy", "", null, "Martin", "  "];

IEnumerable<string> valid = names.OfType<string>()
    .Where(n => !string.IsNullOrWhiteSpace(n));
Console.WriteLine(string.Join(',', valid));

object[] items = [1, "falcon", 2.5, 3, 'c', "owl", 7];

var ints = items.OfType<int>();
Console.WriteLine(string.Join(',', ints));

var strings = items.Where(e => e is string).Cast<string>();
Console.WriteLine(string.Join(',', strings));

We filter a list of nullable strings and an array of objects.

IEnumerable<string> valid = names.OfType<string>()
    .Where(n => !string.IsNullOrWhiteSpace(n));

The OfType<string> method drops the null values; its result is typed as a sequence of non-nullable strings. Then we remove the empty and white-space strings.

var ints = items.OfType<int>();

The OfType method keeps only the elements of the given type.

var strings = items.Where(e => e is string).Cast<string>();

This is a longer equivalent of OfType: we filter by type with Where and convert the elements with Cast.

$ dotnet run
Peter,Lucy,Martin
1,3,7
falcon,owl

C# LINQ Where with dictionary

A dictionary is a sequence of key-value pairs, so it can be filtered with Where as well.

Program.cs
var stock = new Dictionary<string, int>
{
    ["apples"] = 12,
    ["pears"] = 0,
    ["plums"] = 7,
    ["cherries"] = 0,
    ["oranges"] = 31,
};

var available = stock.Where(kv => kv.Value > 0);

foreach (var (fruit, count) in available)
{
    Console.WriteLine($"{fruit}: {count}");
}

var soldOut = stock.Where(kv => kv.Value == 0).Select(kv => kv.Key);
Console.WriteLine($"sold out: {string.Join(", ", soldOut)}");

We have a dictionary of fruits and their stock counts.

var available = stock.Where(kv => kv.Value > 0);

foreach (var (fruit, count) in available)

The predicate receives a KeyValuePair. We deconstruct the pairs in the foreach statement.

var soldOut = stock.Where(kv => kv.Value == 0).Select(kv => kv.Key);

We get the names of the fruits that are sold out.

$ dotnet run
apples: 12
plums: 7
oranges: 31
sold out: pears, cherries

Source

Enumerable.Where method

In this article we have presented the LINQ Where operation.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.