LINQ Basics: Your Magic Filter for Data! 🎯
Imagine you have a huge toy box with hundreds of toys. Some are red, some are blue. Some are cars, some are dolls. Now your mom asks: “Can you find all the red cars?”
You could dig through every single toy one by one… OR you could use magic glasses that instantly show you ONLY the red cars!
LINQ is those magic glasses for your code.
LINQ stands for Language Integrated Query. It’s a superpower in C# that lets you ask questions about your data—and get answers instantly!
🎭 The Two Ways to Ask: Query vs Method Syntax
Think of LINQ like ordering food. You can:
- Talk to the waiter nicely (Query Syntax) — “I would like the pizza from the menu where it has pepperoni.”
- Point and click on an app (Method Syntax) —
Menu.Where(pizza => hasPepperoni).Select(pizza)
Both get you the same pizza! Let’s see how:
Query Syntax (The Storyteller)
Query syntax reads like a sentence. It starts with from and ends with select.
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var evenNumbers = from n in numbers
where n % 2 == 0
select n;
// Result: 2, 4, 6
Read it like a story:
- “From each number
nin numbers…” - “…where
nis even…” - “…select that
n!”
Method Syntax (The Button Clicker)
Method syntax chains methods together using dots.
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers
.Where(n => n % 2 == 0)
.Select(n => n);
// Result: 2, 4, 6
Read it like steps:
- Take
numbers - Filter
.Where()they are even - Pick them with
.Select()
Which Should You Use?
| Query Syntax | Method Syntax |
|---|---|
| Reads like English | Shorter for simple tasks |
| Great for joins | Has more methods available |
| Beginner-friendly | Power-user favorite |
Pro tip: Most C# developers use Method Syntax for simple stuff and Query Syntax when things get complex!
⏰ Deferred vs Eager Execution: Now or Later?
Here’s a mind-bending concept. When you write LINQ, nothing happens yet!
Deferred Execution (The Lazy Cat 🐱)
var numbers = new List<int> { 1, 2, 3 };
var query = numbers.Where(n => n > 1);
// Nothing runs yet! Just a promise.
numbers.Add(4); // Add more data
foreach (var n in query)
{
Console.WriteLine(n);
}
// Output: 2, 3, 4 ← It saw the 4!
Why? LINQ waits until you actually NEED the results. It’s like making a shopping list—you don’t buy until you go to the store!
Eager Execution (The Excited Puppy 🐕)
Want results RIGHT NOW? Use .ToList() or .ToArray():
var numbers = new List<int> { 1, 2, 3 };
var results = numbers
.Where(n => n > 1)
.ToList(); // Execute NOW!
numbers.Add(4);
foreach (var n in results)
{
Console.WriteLine(n);
}
// Output: 2, 3 ← No 4! We already got our results.
graph TD A["Write LINQ Query"] --> B{Deferred or Eager?} B -->|Deferred| C["Query waits..."] C --> D["Loop or ToList called"] D --> E["NOW it runs!"] B -->|Eager with ToList| E
🔍 Where: The Bouncer at the Door
.Where() is like a bouncer at a club. It only lets in data that matches your rules!
string[] fruits = { "apple", "banana",
"cherry", "apricot" };
// Find fruits starting with 'a'
var aFruits = fruits
.Where(f => f.StartsWith("a"));
// Result: apple, apricot
You can stack multiple conditions:
int[] ages = { 5, 12, 17, 21, 30, 8 };
// Find teenagers (13-19)
var teens = ages
.Where(age => age >= 13 && age <= 19);
// Result: 17
Real-World Example
var products = new List<Product>
{
new Product { Name = "Laptop", Price = 999 },
new Product { Name = "Mouse", Price = 25 },
new Product { Name = "Phone", Price = 699 }
};
// Find affordable products under $100
var affordable = products
.Where(p => p.Price < 100);
// Result: Mouse
🎨 Select: The Transformer
.Select() is like a magical machine. You put something in, and something different comes out!
Think of it as a cookie cutter. The dough goes in, but a star-shaped cookie comes out!
int[] numbers = { 1, 2, 3, 4 };
// Double every number
var doubled = numbers
.Select(n => n * 2);
// Result: 2, 4, 6, 8
Pick Just One Property
var people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 }
};
// Get only the names
var names = people
.Select(p => p.Name);
// Result: "Alice", "Bob"
Create New Shapes
var students = new List<Student>
{
new Student { FirstName = "Ana",
LastName = "Smith",
Score = 95 }
};
// Transform into a new shape
var cards = students.Select(s => new
{
FullName = s.FirstName + " " + s.LastName,
Grade = s.Score >= 90 ? "A" : "B"
});
// Result: { FullName = "Ana Smith", Grade = "A" }
graph TD A["Original Data"] --> B[".Select"] B --> C["Transformed Data"] D["{ Name, Age, City }"] --> E[".Select#40;p => p.Name#41;"] E --> F["Name only"]
🌊 SelectMany: Flatten the Waves
Imagine you have boxes inside boxes. .SelectMany() opens ALL the boxes and lays everything flat!
The Problem It Solves
var classes = new List<SchoolClass>
{
new SchoolClass
{
Name = "Math",
Students = new[] { "Alice", "Bob" }
},
new SchoolClass
{
Name = "Art",
Students = new[] { "Carol", "Dan" }
}
};
With regular .Select(), you get lists inside lists:
var nested = classes.Select(c => c.Students);
// Result: [["Alice","Bob"], ["Carol","Dan"]]
// Two separate arrays!
With .SelectMany(), everything becomes ONE flat list:
var allStudents = classes
.SelectMany(c => c.Students);
// Result: ["Alice","Bob","Carol","Dan"]
// One flat list!
Real-World Example: Orders and Items
var orders = new List<Order>
{
new Order
{
Id = 1,
Items = new[] { "Pizza", "Coke" }
},
new Order
{
Id = 2,
Items = new[] { "Burger", "Fries", "Shake" }
}
};
// Get ALL items from ALL orders
var allItems = orders
.SelectMany(o => o.Items);
// Result: Pizza, Coke, Burger, Fries, Shake
Visual Comparison
graph TD subgraph Select A1["Class 1"] --> B1["[Alice, Bob]"] A2["Class 2"] --> B2["[Carol, Dan]"] end subgraph SelectMany C1["Class 1"] --> D["Alice"] C1 --> E["Bob"] C2["Class 2"] --> F["Carol"] C2 --> G["Dan"] end
🎯 Putting It All Together
Let’s combine everything in one powerful query!
var departments = new List<Department>
{
new Department
{
Name = "Engineering",
Employees = new List<Employee>
{
new Employee { Name = "Ada", Salary = 80000 },
new Employee { Name = "Ben", Salary = 65000 }
}
},
new Department
{
Name = "Marketing",
Employees = new List<Employee>
{
new Employee { Name = "Cara", Salary = 55000 },
new Employee { Name = "Dan", Salary = 72000 }
}
}
};
// Find all well-paid employees (>60k)
// and get just their names
var wellPaid = departments
.SelectMany(d => d.Employees) // Flatten all employees
.Where(e => e.Salary > 60000) // Filter by salary
.Select(e => e.Name); // Get only names
// Result: Ada, Ben, Dan
🚀 Quick Reference
| Method | What It Does | Analogy |
|---|---|---|
Where |
Filters data | Bouncer at the door |
Select |
Transforms each item | Cookie cutter |
SelectMany |
Flattens nested collections | Opening all boxes |
| Execution | When It Runs | Keyword |
|---|---|---|
| Deferred | When you iterate | Lazy cat |
| Eager | Immediately | .ToList() / .ToArray() |
💡 Remember This!
- Query Syntax reads like English:
from,where,select - Method Syntax uses dots:
.Where(),.Select() - Deferred = waits until you need it
- Eager =
.ToList()runs it NOW - Where = keeps only matching items
- Select = transforms items into something new
- SelectMany = flattens nested lists into one
You now have the magic glasses! Go filter, transform, and flatten your data like a pro! 🎉
