Query, filter, and transform in-memory data the modern way
When working with collections in C#, developers often rely on loops to find, filter, and manipulate data. But there’s a faster, more expressive approach — LINQ to Objects, which allows you to query arrays and lists as if they were databases.
Here we’ll explore how to write LINQ queries over arrays and lists, demonstrating practical examples, syntax patterns, and best practices for clean and efficient C# code.
🔹 What is LINQ to Objects?
LINQ to Objects allows you to query any in-memory data structure that implements IEnumerable<T> — including arrays, lists, and other generic collections.
Instead of writing repetitive for or foreach loops, you can use declarative queries to describe what you want, not how to get it.
Example:
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };
var evens = from n in numbers
where n % 2 == 0
select n;
foreach (var n in evens)
Console.WriteLine(n);
✅ Output:
2
4
6
8
The from…where…select syntax gives you clean, readable, SQL-like control over arrays and lists.
💡 Arrays vs Lists in LINQ
Both arrays (T[]) and lists (List<T>) implement IEnumerable<T>, so they can be queried in exactly the same way.
The difference lies in mutability and performance characteristics, not in how LINQ operates.
| Collection Type | Description | Example |
|---|---|---|
| Array | Fixed size, fast access | int[] numbers = {1,2,3}; |
| List | Dynamic size, flexible methods | List<int> nums = new() {1,2,3}; |
Both can be filtered, projected, grouped, or ordered using LINQ.
⚙️ Filtering Data with LINQ
The most common operation is filtering — selecting only the items that meet a condition.
List<string> names = new List<string>
{
"Alice", "Bob", "Charlie", "David", "Eve"
};
var shortNames = from name in names
where name.Length <= 4
select name;
foreach (var n in shortNames)
Console.WriteLine(n);
✅ Output:
Bob
Eve
Equivalent method syntax:
var shortNames = names.Where(name => name.Length <= 4);
🔁 Ordering Data
Use orderby (query syntax) or OrderBy() / OrderByDescending() (method syntax) to sort data.
int[] scores = { 70, 85, 90, 60, 95 };
var sortedScores = from s in scores
orderby s descending
select s;
foreach (var s in sortedScores)
Console.WriteLine(s);
✅ Output:
95
90
85
70
60
🧩 Projecting Data with select
The select keyword lets you transform items as you query them — known as projection.
string[] fruits = { "apple", "banana", "cherry" };
var upper = from f in fruits
select f.ToUpper();
foreach (var f in upper)
Console.WriteLine(f);
✅ Output:
APPLE
BANANA
CHERRY
You can also create anonymous types:
var fruitInfo = from f in fruits
select new { Original = f, Length = f.Length };
foreach (var info in fruitInfo)
Console.WriteLine($"{info.Original} ({info.Length})");
✅ Output:
apple (5)
banana (6)
cherry (6)
🧠 Combining Filters and Projections
You can chain multiple LINQ operations together for powerful results.
List<int> numbers = new() { 5, 8, 13, 21, 34, 55, 89 };
var result = from n in numbers
where n > 10 && n % 2 == 1
orderby n descending
select n * 2;
foreach (var r in result)
Console.WriteLine(r);
✅ Output:
178
110
26
This example:
- Filters numbers greater than 10
- Keeps only odd numbers
- Orders them descending
- Doubles each value
All in just one expressive query.
🧮 Aggregating Data
LINQ also supports aggregation — counting, summing, or averaging elements.
int[] ages = { 18, 21, 25, 30, 40 };
Console.WriteLine(ages.Count()); // 5
Console.WriteLine(ages.Average()); // 26.8
Console.WriteLine(ages.Max()); // 40
Console.WriteLine(ages.Min()); // 18
Console.WriteLine(ages.Sum()); // 134
These methods come from the System.Linq namespace and work on any IEnumerable<T>.
🚀 Real-World Example: Working with Objects
LINQ shines when working with lists of complex objects.
class Student
{
public string Name { get; set; }
public int Grade { get; set; }
}
var students = new List<Student>
{
new() { Name = "Alice", Grade = 85 },
new() { Name = "Bob", Grade = 92 },
new() { Name = "Charlie", Grade = 78 }
};
var topStudents = from s in students
where s.Grade >= 80
orderby s.Grade descending
select new { s.Name, s.Grade };
foreach (var s in topStudents)
Console.WriteLine($"{s.Name} - {s.Grade}");
✅ Output:
Bob - 92
Alice - 85
⚡ LINQ Method Syntax Examples
If you prefer method chaining, the same query can be written as:
var topStudents = students
.Where(s => s.Grade >= 80)
.OrderByDescending(s => s.Grade)
.Select(s => new { s.Name, s.Grade });
Both produce identical results — it’s purely a matter of coding style and context.
🧪 Challenge Task
Write a program that:
- Creates a list of cities with population data
- Filters only cities above 100,000 residents
- Orders them by name
- Projects results as anonymous types with name and population formatted nicely
Try it using both query syntax and method syntax.
📚 Summary
| Operation | Description | Example |
|---|---|---|
Where() | Filter data | numbers.Where(n => n > 5) |
Select() | Transform data | names.Select(n => n.ToUpper()) |
OrderBy() | Sort ascending | scores.OrderBy(s => s) |
OrderByDescending() | Sort descending | scores.OrderByDescending(s => s) |
Count() / Sum() / Average() | Aggregate | ages.Average() |
ToList() | Force execution | query.ToList() |
✅ Best Practices
- Always
using System.Linq;at the top of your file - Prefer method syntax when chaining many operations
- Use deferred execution wisely — queries run when enumerated
- Keep queries readable; break into steps if they get long
- Avoid modifying the collection while it’s being queried
🧑💻 Want more?
Join our Coding Workshops – learn to combine filtering, grouping, and joining collections with real-world datasets and performance insights for modern .NET applications.