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.
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.
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.
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.
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.
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.
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.
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.
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.
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
In this article we have presented the LINQ Where operation.
Author
List all C# tutorials.