LINQ (Language-Integrated Query) is one of C#’s most powerful features, letting you filter, transform, sort, and aggregate data with clean, expressive syntax instead of writing manual loops.
This collection of 40 C# LINQ exercises takes you through the most commonly used methods, Where(), Select(), OrderBy(), GroupBy(), and aggregation functions, so you can start writing more concise, readable C# code.
Each exercise includes a Practice Problem, Purpose, Hint, and a full Solution with Explanation, so you understand exactly how each LINQ method transforms your data.
Also, See: C# Exercises with 22 topic-wise sets and 620+ practice questions.
What You’ll Practice
- Filtering & Projection:
.Where()and.Select(). - Sorting & Grouping:
.OrderBy(),.OrderByDescending(), and.GroupBy(). - Aggregation:
.Sum(),.Average(),.Min(),.Max(), and.Count(). - Element Operations:
.FirstOrDefault(),.Any(), and method chaining.
+ Table Of Contents (40 Exercises)
Table of contents
- Exercise 1: Filtering Even Numbers
- Exercise 2: Finding Long Words
- Exercise 3: Sorting Names in Reverse
- Exercise 4: Finding the Top 3 Unique Numbers
- Exercise 5: Paginating a Product List
- Exercise 6: Finding Fruits Starting with A
- Exercise 7: Filtering Ages in a Range
- Exercise 8: Squaring a List of Numbers
- Exercise 9: Finding the First Match
- Exercise 10: Finding the Last Match
- Exercise 11: Converting Strings to Uppercase
- Exercise 12: Projecting Object Properties
- Exercise 13: Creating Anonymous Types
- Exercise 14: Flattening Nested Lists
- Exercise 15: Finding Unique Characters
- Exercise 16: Projecting with Index
- Exercise 17: Zipping Two Arrays Together
- Exercise 18: Extracting Unique Email Domains
- Exercise 19: Flattening a 2D Array
- Exercise 20: Extracting File Extensions
- Exercise 21: Summing Squares of Odd Numbers
- Exercise 22: Averaging Prices with a Condition
- Exercise 23: Finding Min and Max Price
- Exercise 24: Counting Matching Elements
- Exercise 25: Joining Words with Aggregate
- Exercise 26: Calculating Factorial with LINQ
- Exercise 27: Summing Department Salaries
- Exercise 28: Counting Vowels with LINQ
- Exercise 29: Multiplying Numbers with Aggregate
- Exercise 30: Finding the Longest Word
- Exercise 31: Grouping People by Age
- Exercise 32: Counting Words by Length
- Exercise 33: Joining Students and Courses
- Exercise 34: Left Joining Employees and Departments
- Exercise 35: Finding Common Numbers
- Exercise 36: Finding Missing Products
- Exercise 37: Combining Lists Without Duplicates
- Exercise 38: Finding the Most Frequent Number
- Exercise 39: Finding the Highest Paid Employee per Department
- Exercise 40: Splitting a List into Chunks
Exercise 1: Filtering Even Numbers
Practice Problem: Given a list of integers, return only the even numbers.
Purpose: This exercise helps you practice the Where() method, the most fundamental LINQ operator for filtering a sequence down to elements that satisfy a condition.
Given Input: numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
Expected Output:
Filtering even numbers from the list: Even Numbers: 2, 4, 6, 8, 10 Filtering completed successfully.
▼ Hint
- Use the
Where()LINQ method with a lambda expression. - Check each number using the modulus operator:
n % 2 == 0. - Call
ToList()to convert the result into a list.
▼ Solution & Explanation
Explanation:
numbers.Where(n => n % 2 == 0): Uses LINQ’sWhere()method to keep only the elements that satisfy the condition, in this case, numbers divisible by 2 with no remainder.n % 2 == 0: The lambda expression checks each number for evenness..ToList(): Converts the filtered LINQ query into a concreteList<int>.
Exercise 2: Finding Long Words
Practice Problem: Given a list of words, find all words that have more than 5 characters.
Purpose: This exercise helps you practice filtering a sequence of strings based on a property of each element, in this case, its length.
Given Input: words = new List<string> { "cat", "elephant", "dog", "butterfly", "ox", "giraffe" }
Expected Output:
Finding words longer than 5 characters: Long Words: elephant, butterfly, giraffe Filtering completed successfully.
▼ Hint
- Use
Where()with a lambda that checks each word’sLengthproperty. - The condition should be
w.Length > 5. - Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
words.Where(w => w.Length > 5): Filters the list to keep only words whoseLengthproperty is greater than 5.w.Length: Accesses each string’s character count directly within the lambda..ToList(): Materializes the filtered results into aList<string>.
Exercise 3: Sorting Names in Reverse
Practice Problem: Given a list of names, sort them in descending alphabetical order.
Purpose: This exercise helps you practice the OrderByDescending() method for sorting a sequence in reverse order using the elements’ natural comparison.
Given Input: names = new List<string> { "Charlie", "Alice", "Bob", "Eve", "Dave" }
Expected Output:
Sorting names in descending alphabetical order: Sorted Names: Eve, Dave, Charlie, Bob, Alice Sorting completed successfully.
▼ Hint
- Use
OrderByDescending()instead ofOrderBy()to sort in reverse order. - Since you’re sorting the strings themselves, the lambda can simply be
name => name. - Call
ToList()to get the final sorted list.
▼ Solution & Explanation
Explanation:
names.OrderByDescending(name => name): Sorts the strings using their natural alphabetical comparison, but in reverse (Z to A) order.- The lambda
name => name: Tells LINQ to use each string’s own value as the sort key, rather than some derived property. .ToList(): Converts the sorted sequence into a concrete list.
Exercise 4: Finding the Top 3 Unique Numbers
Practice Problem: From an array of integers, select the top 3 highest unique values.
Purpose: This exercise helps you practice chaining multiple LINQ methods together, Distinct(), OrderByDescending(), and Take(), to build a multi-step query.
Given Input: numbers = new int[] { 50, 20, 50, 80, 80, 10, 30, 90 }
Expected Output:
Finding the top 3 highest unique values: Top 3 Unique Numbers: 90, 80, 50 Search completed successfully.
▼ Hint
- Use
Distinct()first to remove duplicate values. - Chain
OrderByDescending()to sort the unique values from highest to lowest. - Use
Take(3)to grab the top three, then convert withToArray()orToList().
▼ Solution & Explanation
Explanation:
numbers.Distinct(): Removes duplicate values from the array first, so repeated numbers like 50 and 80 only count once..OrderByDescending(n => n): Sorts the remaining unique values from highest to lowest..Take(3): Selects only the first three values from the sorted sequence, the three highest unique numbers..ToArray(): Converts the final LINQ query into a concrete array.
Exercise 5: Paginating a Product List
Practice Problem: Given a list of 100 products, skip the first 20 and take the next 10.
Purpose: This exercise helps you practice the Skip-then-Take pattern, the standard way to extract a single page of results from a larger sequence.
Given Input: A list of 100 products named “Product 1” through “Product 100”
Expected Output:
Skipping the first 20 products and taking the next 10: Result: Product 21, Product 22, Product 23, Product 24, Product 25, Product 26, Product 27, Product 28, Product 29, Product 30 Pagination completed successfully.
▼ Hint
- Use
Enumerable.Range()combined withSelect()to quickly generate 100 sample products. - Chain
Skip(20)to skip over the first 20 items. - Chain
Take(10)afterSkip()to grab the next 10 items.
▼ Solution & Explanation
Explanation:
Enumerable.Range(1, 100).Select(i => $"Product {i}"): Generates a sequence of 100 product names to simulate a large dataset.products.Skip(20): Skips over the first 20 elements in the list, leaving everything from the 21st item onward..Take(10): Selects only the next 10 items after the skipped ones.- Together:
Skip()andTake()extract exactly one page of results from a larger sequence.
Exercise 6: Finding Fruits Starting with A
Practice Problem: Find all fruits in a string array that start with the letter ‘A’ (case-insensitive).
Purpose: This exercise helps you practice combining Where() with StartsWith() and a case-insensitive comparison option, so casing differences don’t affect the match.
Given Input: fruits = new string[] { "apple", "Banana", "Avocado", "cherry", "apricot", "Mango" }
Expected Output:
Finding fruits that start with the letter 'A': Fruits: apple, Avocado, apricot Search completed successfully.
▼ Hint
- Use
Where()with a lambda that callsStartsWith("A", ...)on each fruit. - Pass
StringComparison.OrdinalIgnoreCaseas the second argument to make the check case-insensitive. - Convert the result to an array or list as needed.
▼ Solution & Explanation
Explanation:
fruits.Where(f => f.StartsWith("A", StringComparison.OrdinalIgnoreCase)): Filters the array to keep only strings that begin with “A”, ignoring whether the actual letter is upper or lower case.StringComparison.OrdinalIgnoreCase: TellsStartsWith()to treat “a” and “A” as equivalent during the comparison.- Why this matters: Without this comparison option,
StartsWith()would be case-sensitive by default and miss lowercase entries like “apple”.
Exercise 7: Filtering Ages in a Range
Practice Problem: Given a list of employee ages, find all employees between the ages of 25 and 35 (inclusive).
Purpose: This exercise helps you practice combining two conditions inside a single Where() lambda using the logical && operator.
Given Input: ages = new List<int> { 22, 25, 30, 35, 40, 28, 19, 33 }
Expected Output:
Finding employees aged between 25 and 35 (inclusive): Ages: 25, 30, 35, 28, 33 Range filtering completed successfully.
▼ Hint
- Use
Where()with a lambda that checks two conditions combined with&&. - The condition should be
age >= 25 && age <= 35to make both boundary values inclusive. - Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
ages.Where(age => age >= 25 && age <= 35): Keeps only the ages that fall within the inclusive range, using two comparisons combined with&&.age >= 25 && age <= 35: Both conditions must be true for an age to be included; "inclusive" means the boundary values 25 and 35 themselves qualify..ToList(): Converts the filtered sequence into aList<int>.
Exercise 8: Squaring a List of Numbers
Practice Problem: Take a list of integers and return a list of their squares.
Purpose: This exercise helps you practice the Select() method, LINQ's projection operator, used to transform each element of a sequence into a new value.
Given Input: numbers = new List<int> { 1, 2, 3, 4, 5 }
Expected Output:
Squaring each number in the list: Squares: 1, 4, 9, 16, 25 Transformation completed successfully.
▼ Hint
- Use
Select()instead ofWhere(), since you're transforming values rather than filtering them. - The lambda should square each number:
n => n * n. - Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
numbers.Select(n => n * n): Transforms each element in the list into a new value, in this case, its square, without changing the original list.n * n: The lambda expression squares each number as it passes through the projection..ToList(): Converts the projected sequence into a newList<int>.
Exercise 9: Finding the First Match
Practice Problem: Find the first number in a list that is divisible by 7. Return a default value if none exist.
Purpose: This exercise helps you practice using FirstOrDefault() instead of First(), so your program doesn't throw an exception when no element matches the condition.
Given Input: numbers = new List<int> { 10, 15, 22, 33, 41, 50 }
Expected Output:
Finding the first number divisible by 7: Result: 0 Search completed successfully.
▼ Hint
- Use
FirstOrDefault()instead ofFirst(), since a match isn't guaranteed to exist. - The condition should check
n % 7 == 0. - If no element matches,
FirstOrDefault()returns the type's default value (0 forint) instead of throwing an exception.
▼ Solution & Explanation
Explanation:
numbers.FirstOrDefault(n => n % 7 == 0): Searches for the first number in the list matching the condition, and returns that value if found.- No match found: None of the numbers in this list are evenly divisible by 7.
- Fallback behavior: Since
intis a value type,FirstOrDefault()falls back to its default value, 0, instead of throwing an exception likeFirst()would. - Why this matters: This makes
FirstOrDefault()a safer choice thanFirst()whenever a match isn't guaranteed to exist.
Exercise 10: Finding the Last Match
Practice Problem: Find the last word in a list that ends with the letter 'e'.
Purpose: This exercise helps you practice using LastOrDefault() to find the final element matching a condition, scanning the sequence for every match but keeping only the last one.
Given Input: words = new List<string> { "apple", "banana", "grape", "cherry", "orange", "kiwi" }
Expected Output:
Finding the last word that ends with the letter 'e': Result: orange Search completed successfully.
▼ Hint
- Use
LastOrDefault()to find the last matching element instead of the first. - The condition should check
w.EndsWith("e"). LastOrDefault()safely returnsnull(for strings) if no match is found, rather than throwing an exception.
▼ Solution & Explanation
Explanation:
words.LastOrDefault(w => w.EndsWith("e")): Scans the list for every word ending in "e", but keeps only the last one found instead of the first.w.EndsWith("e"): Checks whether each string's final character is "e".LastOrDefault()(instead ofLast()): Returnsnullinstead of throwing an exception if no word in the list matched the condition, sincestringis a reference type.
Exercise 11: Converting Strings to Uppercase
Practice Problem: Transform a list of lowercase strings into uppercase.
Purpose: This exercise helps you practice using Select() to apply a simple transformation, calling a built-in string method, to every element in a sequence.
Given Input: words = new List<string> { "apple", "banana", "cherry" }
Expected Output:
Converting words to uppercase: Result: APPLE, BANANA, CHERRY Conversion completed successfully.
▼ Hint
- Use
Select()to transform each string. - Call
.ToUpper()on each word inside the lambda. - Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
words.Select(w => w.ToUpper()): Projects each string in the list into its uppercase equivalent.w.ToUpper(): Converts an individual string to uppercase within the lambda..ToList(): Converts the transformed sequence into a newList<string>, leaving the original list unchanged.
Exercise 12: Projecting Object Properties
Practice Problem: Given a list of User objects (with Id, Name, Email), project a new list containing only the Email strings.
Purpose: This exercise helps you practice using Select() to pull a single property out of a list of custom objects, producing a simpler, more focused collection.
Given Input: Users Alice, Bob, and Charlie, each with an Id, Name, and Email
Expected Output:
Extracting email addresses from user objects: Emails: alice@example.com, bob@example.com, charlie@example.com Projection completed successfully.
▼ Hint
- Use
Select()with a lambda that accesses only the Email property. - The lambda should be
u => u.Email. - Convert the projected sequence to a list with
ToList().
▼ Solution & Explanation
Explanation:
users.Select(u => u.Email): Projects eachUserobject down to just its Email property, discarding the Id and Name.u.Email: Accesses a single property of each object within the lambda.- Result: A new
List<string>, entirely separate from the original list of User objects.
Exercise 13: Creating Anonymous Types
Practice Problem: Convert a list of Product objects into a list of anonymous types containing only ProductName and CalculatedTax (Price * 0.15).
Purpose: This exercise helps you practice projecting into an anonymous type, a quick way to reshape data into a new, temporary structure without defining a separate class.
Given Input: Laptop ($1000), Mouse ($20), Keyboard ($50)
Expected Output:
Projecting products into anonymous types with calculated tax: Laptop: Tax = $150.00 Mouse: Tax = $3.00 Keyboard: Tax = $7.50 Anonymous type projection completed successfully.
▼ Hint
- Use
Select()with a lambda that returnsnew { ... }instead of an existing class. - Give the anonymous type two properties:
ProductNameandCalculatedTax. - Calculate
CalculatedTaxasp.Price * 0.15mdirectly inside the projection.
▼ Solution & Explanation
Explanation:
products.Select(p => new { ProductName = ..., CalculatedTax = ... }): Projects each Product into a new anonymous type containing only the two computed fields needed.new { ... }(without a class name): Creates an anonymous type on the fly, useful when you only need a temporary shape for your data.p.Price * 0.15m: Calculates the tax for each product as part of the projection itself.var productTaxInfo: Since anonymous types have no explicit name,varis required to hold the result.
Exercise 14: Flattening Nested Lists
Practice Problem: Given a list of Department objects, where each department has a List<Employee>, flatten this into a single IEnumerable<Employee>.
Purpose: This exercise helps you practice using SelectMany() to flatten a collection of collections into one single sequence, rather than a sequence of lists.
Given Input: Engineering (Alice, Bob), Sales (Charlie), Marketing (Dave, Eve)
Expected Output:
Flattening all employees across every department: All Employees: Alice, Bob, Charlie, Dave, Eve Flattening completed successfully.
▼ Hint
- Use
SelectMany()instead ofSelect()to flatten a collection of collections. - The lambda should return each department's Employees list:
d => d.Employees. - The result is a single flat
IEnumerable<Employee>, not a list of lists.
▼ Solution & Explanation
Explanation:
departments.SelectMany(d => d.Employees): Instead of returning a list of lists,SelectMany()combines every department's Employees collection into a single, flat sequence.d.Employees: For each department, this selects its nested list of employees, whichSelectMany()then flattens automatically.IEnumerable<Employee> allEmployees: The result type is a single flat sequence of Employee objects, regardless of how many departments or employees per department existed.- Why not
Select(): UsingSelect()instead ofSelectMany()here would have produced a sequence of lists instead of a single flattened sequence.
Exercise 15: Finding Unique Characters
Practice Problem: Given a sentence, extract a list of all unique characters used, sorted alphabetically.
Purpose: This exercise helps you practice treating a string as a sequence of characters in LINQ, chaining Where(), Distinct(), and OrderBy() together.
Given Input: sentence = "the quick brown fox"
Expected Output:
Extracting unique characters from "the quick brown fox": Unique Characters: b, c, e, f, h, i, k, n, o, q, r, t, u, w, x Extraction completed successfully.
▼ Hint
- Remember that a string can be enumerated directly as a sequence of characters in LINQ.
- Use
Where()to exclude spaces, thenDistinct()to remove duplicate letters. - Finish with
OrderBy(c => c)to sort the remaining characters alphabetically.
▼ Solution & Explanation
Explanation:
sentence.Where(c => c != ' '): Since a string can be treated as a sequence of characters, this filters out spaces before processing..Distinct(): Removes any duplicate characters, keeping only the first occurrence of each..OrderBy(c => c): Sorts the remaining unique characters into alphabetical order.- Why chain them: These three operations together produce exactly the unique letters used in the sentence, sorted alphabetically.
Exercise 16: Projecting with Index
Practice Problem: Given a list of strings, return a new string array where each item looks like "Index: {index}, Value: {value}".
Purpose: This exercise helps you practice using the overload of Select() that provides each element's position, useful when the index itself is part of the output.
Given Input: items = new List<string> { "Red", "Green", "Blue" }
Expected Output:
Projecting each item with its index: Index: 0, Value: Red Index: 1, Value: Green Index: 2, Value: Blue Index projection completed successfully.
▼ Hint
- Use the overload of
Select()that takes a lambda with two parameters:(value, index). - Build a formatted string inside the lambda using both parameters.
- Convert the result to an array with
ToArray().
▼ Solution & Explanation
Explanation:
items.Select((value, index) => ...): Uses the overload ofSelect()that provides both the element and its zero-based position in the sequence.(value, index) =>: The lambda takes two parameters instead of one, the item itself and its index.$"Index: {index}, Value: {value}": Combines both pieces of information into a single formatted string for each element..ToArray(): Converts the projected sequence into a string array.
Exercise 17: Zipping Two Arrays Together
Practice Problem: Use Zip to combine an array of first names and an array of last names into a single "First Last" string array.
Purpose: This exercise helps you practice using Zip() to pair up elements from two sequences by their position and combine them into a single result.
Given Input: firstNames = "John", "Jane", "Jim", lastNames = "Doe", "Smith", "Brown"
Expected Output:
Combining first and last names using Zip: Full Names: John Doe, Jane Smith, Jim Brown Zipping completed successfully.
▼ Hint
- Use
Zip()to combine two sequences element by element based on their position. - Pass the second array and a lambda that combines each pair:
(first, last) => $"{first} {last}". - The result pairs up elements at matching positions from both arrays.
▼ Solution & Explanation
Explanation:
firstNames.Zip(lastNames, (first, last) => ...): Pairs up elements from two sequences by their position, combining the first element of each, then the second element of each, and so on.(first, last) => $"{first} {last}": The lambda defines how each paired-up pair of elements should be combined into a single result.- Mismatched lengths: If the two arrays had different lengths,
Zip()would only produce as many pairs as the shorter sequence has elements. .ToArray(): Converts the resulting sequence of combined names into a string array.
Exercise 18: Extracting Unique Email Domains
Practice Problem: Given a list of email addresses, extract a unique list of email domains.
Purpose: This exercise helps you practice combining string parsing with Select() and Distinct() to extract and deduplicate a derived piece of data.
Given Input: emails = "alice@gmail.com", "bob@yahoo.com", "charlie@gmail.com", "dave@outlook.com", "eve@yahoo.com"
Expected Output:
Extracting unique email domains: Domains: gmail.com, yahoo.com, outlook.com Domain extraction completed successfully.
▼ Hint
- Use
Select()with a lambda that splits each email on'@'and takes the second part:email.Split('@')[1]. - Chain
Distinct()afterSelect()to remove duplicate domains. - Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
email.Split('@')[1]: Splits each email address on the '@' character and takes the second part, which is the domain..Select(email => ...): Applies this splitting logic to every email address in the list, projecting each one down to just its domain..Distinct(): Removes duplicate domains, since multiple emails often share the same provider.- Result: The final list contains each domain exactly once, in the order it was first encountered.
Exercise 19: Flattening a 2D Array
Practice Problem: Flatten a 2D integer array (int[][]) into a 1D sequence of integers.
Purpose: This exercise helps you practice using SelectMany() on a jagged array, where each row can have a different length, to produce one flat sequence.
Given Input: matrix = { {1, 2, 3}, {4, 5}, {6, 7, 8, 9} }
Expected Output:
Flattening a jagged 2D array into a single sequence: Flattened: 1, 2, 3, 4, 5, 6, 7, 8, 9 Flattening completed successfully.
▼ Hint
- Use
SelectMany()to flatten the array of arrays into a single sequence. - The lambda can simply return each row directly:
row => row. - Convert the result to an array with
ToArray().
▼ Solution & Explanation
Explanation:
int[][] matrix: A jagged array, an array where each row is itself a separate array, and rows can have different lengths.matrix.SelectMany(row => row): Combines every row's elements into a single flat sequence, rather than a sequence of arrays.row => row: Since each row is already a sequence of integers, the lambda simply returns it directly forSelectMany()to flatten..ToArray(): Converts the flattened sequence into a single one-dimensionalintarray.
Exercise 20: Extracting File Extensions
Practice Problem: Given a list of file paths, extract just the file extensions dynamically.
Purpose: This exercise helps you practice combining LastIndexOf() and Substring() inside a Select() projection to extract a variable-length piece of each string.
Given Input: filePaths = "document.txt", "image.png", "archive.zip", "notes.txt", "photo.jpeg"
Expected Output:
Extracting file extensions dynamically: Extensions: .txt, .png, .zip, .txt, .jpeg Extraction completed successfully.
▼ Hint
- Use
path.LastIndexOf('.')to find where the extension begins. - Use
path.Substring()starting from that index to extract the extension, including the period. - Apply this inside a
Select()lambda to process the whole list.
▼ Solution & Explanation
Explanation:
path.LastIndexOf('.'): Finds the position of the final period in the file name, which marks the start of the extension.path.Substring(...): Extracts everything from that position to the end of the string, capturing the extension along with its leading period..Select(path => ...): Applies this extraction logic to every file path in the list.- Why
LastIndexOf(): UsingLastIndexOf()rather thanIndexOf()correctly handles file names that contain multiple periods, such as "archive.tar.gz".
Exercise 21: Summing Squares of Odd Numbers
Practice Problem: Calculate the sum of the squares of all odd numbers in a list.
Purpose: This exercise helps you practice using Sum() with a selector, transforming and totaling values in a single step instead of chaining a separate Select() beforehand.
Given Input: numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
Expected Output:
Calculating the sum of squares of all odd numbers: Sum of Squares = 165 Calculation completed successfully.
▼ Hint
- Use
Where()to filter down to only the odd numbers first. - Chain
Sum()with a lambda that squares each number:n => n * n. Sum()'s selector overload lets you transform and total values in a single step.
▼ Solution & Explanation
Explanation:
numbers.Where(n => n % 2 != 0): Filters the list down to only odd numbers first..Sum(n => n * n): Squares each remaining number and adds all the squares together in a single step.- Why this works: Combining a selector directly inside
Sum()avoids needing a separateSelect()call before it.
Exercise 22: Averaging Prices with a Condition
Practice Problem: Given a list of Book objects, find the average price of books published after the year 2010.
Purpose: This exercise helps you practice combining Where() with Average(), filtering a collection before aggregating it.
Given Input: Clean Code (2008, $35), The Pragmatic Programmer (2019, $40), C# in Depth (2019, $45), Design Patterns (1994, $50), Refactoring (2018, $42)
Expected Output:
Calculating average price of books published after 2010: Average Price = $42.33 Calculation completed successfully.
▼ Hint
- Use
Where()to filter books published after the year 2010. - Chain
Average()with a lambda that selects the Price property:b => b.Price. Average()computes the mean directly over the filtered results.
▼ Solution & Explanation
Explanation:
books.Where(b => b.Year > 2010): Filters the list down to only books published after 2010..Average(b => b.Price): Calculates the average of the Price property across the filtered books.- Why this works: Because
Average()accepts a selector directly, there's no need for a separateSelect()step before calling it.
Exercise 23: Finding Min and Max Price
Practice Problem: Find the minimum and maximum price in a list of Product objects.
Purpose: This exercise helps you practice using Min() and Max() with a selector to find the smallest and largest value of a specific property across a collection.
Given Input: Laptop ($999), Mouse ($15), Monitor ($250), Keyboard ($45)
Expected Output:
Finding the cheapest and most expensive product: Cheapest Price = $15 Most Expensive Price = $999 Price range search completed successfully.
▼ Hint
- Use
Min()with a lambda selecting the Price property to find the cheapest product's price. - Use
Max()the same way to find the most expensive product's price. - Both methods scan the whole collection in a single pass to find their result.
▼ Solution & Explanation
Explanation:
products.Min(p => p.Price): Scans every product and returns the smallest Price value found.products.Max(p => p.Price): Scans every product and returns the largest Price value found.- Why a selector: Both methods accept a selector, so they can compute the minimum or maximum of a specific property, rather than requiring the objects themselves to be directly comparable.
Exercise 24: Counting Matching Elements
Practice Problem: Count how many elements in a list of strings contain the word "LINQ".
Purpose: This exercise helps you practice using Count() with a predicate, which filters and counts in a single operation instead of chaining Where().Count().
Given Input: "I love LINQ", "This has nothing", "LINQ is powerful", "Another sentence", "LINQ makes life easier"
Expected Output:
Counting how many sentences mention "LINQ": Count = 3 Counting completed successfully.
▼ Hint
- Use
Count()with a lambda instead ofWhere().Count(), since it does both in one step. - The condition should check
s.Contains("LINQ"). Count()with a predicate returns the number of matching elements directly as anint.
▼ Solution & Explanation
Explanation:
sentences.Count(s => s.Contains("LINQ")): Counts how many elements in the list satisfy the given condition, without needing to first filter and then measure the length separately.s.Contains("LINQ"): Checks whether each string includes the substring "LINQ" anywhere within it.- Why this works:
Count()'s selector overload combines filtering and counting into a single operation.
Exercise 25: Joining Words with Aggregate
Practice Problem: Use the Aggregate operator to concatenate a list of words into a single comma-separated sentence, without a trailing comma.
Purpose: This exercise helps you practice Aggregate(), which applies a function cumulatively over a sequence, carrying forward an accumulated result from one element to the next.
Given Input: words = new List<string> { "apple", "banana", "cherry", "date" }
Expected Output:
Joining words into a comma-separated sentence using Aggregate: Result: apple, banana, cherry, date Concatenation completed successfully.
▼ Hint
- Use
Aggregate()with a lambda that takes two parameters: the accumulated result so far and the next element. - Combine them with
(current, next) => current + ", " + next. - Since
Aggregate()only inserts the separator between elements, there's no trailing comma to worry about.
▼ Solution & Explanation
Explanation:
words.Aggregate((current, next) => ...): Applies a function cumulatively over the sequence, carrying forward an accumulated result from one element to the next.current: Holds the combined result built so far, starting as the first word in the list.next: Represents each subsequent word being folded into the accumulated result.current + ", " + next: Appends a comma and space before adding each new word, naturally avoiding a trailing comma since the separator is only added between elements, never after the last one.
Exercise 26: Calculating Factorial with LINQ
Practice Problem: Use LINQ (Enumerable.Range and Aggregate) to calculate the factorial of a given number n.
Purpose: This exercise helps you practice combining Enumerable.Range() with the seeded overload of Aggregate(), which lets you specify an explicit starting value for the accumulation.
Given Input: n = 5
Expected Output:
Calculating the factorial of 5 using LINQ: Factorial = 120 Calculation completed successfully.
▼ Hint
- Use
Enumerable.Range(1, n)to generate the numbers from 1 to n. - Use the overload of
Aggregate()that takes a seed value, starting at1L. - Multiply the accumulator by each number in the lambda:
(accumulator, current) => accumulator * current.
▼ Solution & Explanation
Explanation:
Enumerable.Range(1, n): Generates a sequence of integers from 1 up to n, exactly the numbers needed to compute a factorial.Aggregate(1L, (accumulator, current) => accumulator * current): Starts with a seed value of 1, then multiplies it by each number in the sequence in turn.- The seed value
1L: Provides the starting point for the accumulation and also determines that the result type islong, allowing for larger factorial values thanintcould hold. - Why a seed: This overload of
Aggregate(), which accepts an explicit seed, is useful whenever the accumulation needs a specific starting value rather than just the sequence's first element.
Exercise 27: Summing Department Salaries
Practice Problem: Calculate the total monthly salary expense for a specific department from a list of Employee objects.
Purpose: This exercise helps you practice chaining Where() and Sum() together to total a property across only a filtered subset of a collection.
Given Input: Alice (Engineering, $8500), Bob (Sales, $6000), Charlie (Engineering, $9200), Dave (Marketing, $5500), Eve (Engineering, $7800)
Expected Output:
Calculating total monthly salary for the Engineering department: Total Salary = $25500 Calculation completed successfully.
▼ Hint
- Use
Where()to filter employees down to the target department. - Chain
Sum()with a lambda selecting the Salary property:e => e.Salary. - The result is the total salary expense for just that department.
▼ Solution & Explanation
Explanation:
employees.Where(e => e.Department == "Engineering"): Filters the list down to only employees belonging to the target department..Sum(e => e.Salary): Adds together the Salary property of every employee that passed the filter.- Why chain them: Chaining
Where()andSum()together produces the total salary bill for just that one department, without affecting the original list.
Exercise 28: Counting Vowels with LINQ
Practice Problem: Given a string, count how many vowels (a, e, i, o, u) it contains using LINQ.
Purpose: This exercise helps you practice treating a string as a character sequence and using Count() with a predicate that checks membership in a small reference set.
Given Input: text = "Programming in LINQ is enjoyable"
Expected Output:
Counting vowels in "Programming in LINQ is enjoyable": Vowel Count = 10 Counting completed successfully.
▼ Hint
- Convert the string to lowercase first with
ToLower(). - Use
Count()with a lambda that checks"aeiou".Contains(c)for each character. - A string can be enumerated character by character, so LINQ methods like
Count()work on it directly.
▼ Solution & Explanation
Explanation:
text.ToLower(): Converts the string to lowercase first, so vowel comparisons only need to check one case..Count(c => "aeiou".Contains(c)): Counts how many characters in the string are found within the "aeiou" reference string."aeiou".Contains(c): Checks each character intextagainst the five vowels in a single, compact condition.- Why this works: Treating the string as a sequence of characters lets
Count()work directly on it, just like it would on any other LINQ sequence.
Exercise 29: Multiplying Numbers with Aggregate
Practice Problem: Calculate the product of all non-zero numbers in an array.
Purpose: This exercise helps you practice using Aggregate() to compute a running product, since LINQ has no built-in Product() method the way it has Sum().
Given Input: numbers = new int[] { 2, 0, 3, 4, 0, 5 }
Expected Output:
Calculating the product of all non-zero numbers: Product = 120 Calculation completed successfully.
▼ Hint
- Use
Where()to filter out any zero values before multiplying. - LINQ has no built-in
Product()method, so useAggregate()with a seed of1Linstead. - Multiply the accumulator by each number in the lambda:
(accumulator, current) => accumulator * current.
▼ Solution & Explanation
Explanation:
numbers.Where(n => n != 0): Filters out any zero values first, since multiplying by zero would collapse the entire product to zero..Aggregate(1L, (accumulator, current) => accumulator * current): Starts with a seed value of 1 and multiplies it by each remaining number in turn.- The seed value of 1: Ensures the multiplication starts correctly, since 1 is the multiplicative identity, just as 0 is used as the seed for a sum.
- Why
Aggregate(): There's no built-inProduct()method in LINQ, soAggregate()is the standard way to compute a running product.
Exercise 30: Finding the Longest Word
Practice Problem: Find the longest word in an array of strings using MaxBy.
Purpose: This exercise helps you practice using MaxBy(), which returns the original element with the largest selected value, rather than Max(), which only returns the value itself.
Given Input: words = new string[] { "cat", "elephant", "dog", "hippopotamus", "ox" }
Expected Output:
Finding the longest word in the array: Longest Word: hippopotamus Search completed successfully.
▼ Hint
- Use
MaxBy()with a lambda selecting the Length property:w => w.Length. - Unlike
Max(),MaxBy()returns the original element, not just the maximum value. - This directly gives you the longest word itself, not just its length.
▼ Solution & Explanation
Explanation:
words.MaxBy(w => w.Length): Scans the array and returns the element that produces the largest value for the given selector, in this case, the word with the greatest Length.- Difference from
Max(): UnlikeMax(), which would return the largest Length value itself (a number),MaxBy()returns the original element that has that maximum value. - Why this matters: This makes
MaxBy()the more direct choice whenever you need the item itself, rather than just the value used to compare it.
Exercise 31: Grouping People by Age
Practice Problem: Given a list of Person objects, group them by their Age.
Purpose: This exercise helps you practice the basics of GroupBy(), organizing a flat list of objects into groups that share a common property value.
Given Input: Alice (30), Bob (25), Charlie (30), Dave (25), Eve (35)
Expected Output:
Grouping people by age: Age 30: Alice, Charlie Age 25: Bob, Dave Age 35: Eve Grouping completed successfully.
▼ Hint
- Use
GroupBy()with a lambda selecting the Age property. - Loop through the resulting groups; each one has a
Key(the age) and a collection of matching people. - Use
group.Select()to pull out just the names within each group.
▼ Solution & Explanation
Explanation:
people.GroupBy(p => p.Age): Organizes the list into groups, one for each distinct Age value found.group.Key: Within each group, this holds the Age value that all the people in that group share.group.Select(p => p.Name): Projects each group down to just the names of the people inside it.- Order of groups: Groups appear in the order their key was first encountered while scanning the original list, not sorted numerically.
Exercise 32: Counting Words by Length
Practice Problem: Group a list of words by their length, and display the length along with the count of words that match that length.
Purpose: This exercise helps you practice combining GroupBy() with Count() to summarize how many items fall into each group.
Given Input: words = "cat", "dog", "fish", "bird", "ant", "lion", "owl", "bee"
Expected Output:
Grouping words by length and counting each group: Length 3: 5 words Length 4: 3 words Counting completed successfully.
▼ Hint
- Use
GroupBy()with a lambda selecting each word's Length. - For each group, call
group.Count()to get how many words share that length. - Project the results into an anonymous object containing the length and its count.
▼ Solution & Explanation
Explanation:
words.GroupBy(w => w.Length): Groups the words based on their character count.group.Count(): Counts how many words fall into each individual group.new { Length = group.Key, Count = group.Count() }: Projects each group into an anonymous type pairing the length with how many words share that length.
Exercise 33: Joining Students and Courses
Practice Problem: Given a list of Student objects and Course objects, perform an inner join to list student names alongside their courses.
Purpose: This exercise helps you practice Join(), LINQ's equivalent of a SQL inner join, matching elements from two sequences based on a shared key.
Given Input: Students: (1, Alice), (2, Bob), (3, Charlie). Courses: (1, Math), (1, Science), (2, History), (4, Art)
Expected Output:
Performing an inner join between students and courses: Alice: Math Alice: Science Bob: History Inner join completed successfully.
▼ Hint
- Use
Join(), passing the second collection, a key selector for each side, and a result selector. - The key selectors should return
StudentIdfrom both the student and the course. - Only pairs where both a student and a course share the same
StudentIdappear in the result.
▼ Solution & Explanation
Explanation:
students.Join(courses, student => student.StudentId, course => course.StudentId, (student, course) => ...): Matches each student to every course sharing the sameStudentId, similar to a SQL inner join.- The two key selectors: Specify which property from each sequence should be used as the join key.
(student, course) => new { student.Name, course.CourseName }: Defines the shape of the combined result for each matching pair.- Result: Charlie has no courses and the course for StudentId 4 has no matching student, so neither appears in the result; only pairs where both sides match are included.
Exercise 34: Left Joining Employees and Departments
Practice Problem: Perform a left outer join between Employees and Departments so that employees without a department are still listed.
Purpose: This exercise helps you practice simulating a left outer join in LINQ using GroupJoin() combined with SelectMany() and DefaultIfEmpty(), since LINQ has no direct left-join method.
Given Input: Employees: Alice (Dept 10), Bob (Dept 20), Charlie (Dept 0, no matching department). Departments: 10 (Engineering), 20 (Sales)
Expected Output:
Performing a left outer join between employees and departments: Alice: Engineering Bob: Sales Charlie: No Department Left outer join completed successfully.
▼ Hint
- Use
GroupJoin()instead ofJoin(), so employees without a match still produce a group (an empty one). - Chain
SelectMany()withDefaultIfEmpty()on each employee's matched departments to flatten the groups while keeping unmatched employees. - Use the null-conditional operator (
department?.DepartmentName) combined with??to supply a fallback label for unmatched employees.
▼ Solution & Explanation
Explanation:
employees.GroupJoin(departments, ...): Groups each employee with all matching departments (zero or more), unlikeJoin(), which drops employees with no match at all..SelectMany(x => x.MatchedDepartments.DefaultIfEmpty(), ...): Flattens each employee's group of matches, andDefaultIfEmpty()inserts a singlenullplaceholder if an employee had no matching department.department?.DepartmentName ?? "No Department": Uses the null-conditional and null-coalescing operators together to safely fall back to a default label when there is no matching department.- Result: Because Charlie's DepartmentId of 0 doesn't match any real department's Id, he still appears in the result with "No Department" instead of being dropped entirely.
Exercise 35: Finding Common Numbers
Practice Problem: Find the numbers that are present in both List A and List B.
Purpose: This exercise helps you practice using Intersect() to find elements shared by two sequences in a single method call.
Given Input: listA = { 1, 2, 3, 4, 5, 6 }, listB = { 4, 5, 6, 7, 8, 9 }
Expected Output:
Finding numbers present in both lists: Common Numbers: 4, 5, 6 Intersection completed successfully.
▼ Hint
- Use
Intersect(), passing the second list as an argument. - The result contains only elements that exist in both sequences.
- Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
listA.Intersect(listB): Returns only the elements that appear in bothlistAandlistB, using each element's default equality comparison.- Result: Contains each common value exactly once, even if it appeared multiple times in either original list.
.ToList(): Converts the resulting sequence into a concreteList<int>.
Exercise 36: Finding Missing Products
Practice Problem: Find all products in Inventory List A that are completely missing from Inventory List B.
Purpose: This exercise helps you practice using Except() to find elements that exist in one sequence but not another.
Given Input: inventoryA = "Apple", "Banana", "Cherry", "Date", "Elderberry", inventoryB = "Banana", "Date", "Fig"
Expected Output:
Finding products in Inventory A that are missing from Inventory B: Missing Products: Apple, Cherry, Elderberry Difference calculation completed successfully.
▼ Hint
- Use
Except(), passing the second list as an argument. - The result contains only elements from the first list that are not present in the second.
- Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
inventoryA.Except(inventoryB): Returns every element frominventoryAthat does not appear anywhere ininventoryB.- Result: Elements that exist in both lists, like "Banana" and "Date", are removed from the result entirely.
.ToList(): Converts the resulting sequence into a concreteList<string>.
Exercise 37: Combining Lists Without Duplicates
Practice Problem: Combine two lists of student names into a single list, ensuring no duplicate names exist in the final result.
Purpose: This exercise helps you practice using Union() to merge two sequences while automatically removing duplicates.
Given Input: listA = "Alice", "Bob", "Charlie", listB = "Bob", "Dave", "Alice", "Eve"
Expected Output:
Combining two lists of names without duplicates: Combined Names: Alice, Bob, Charlie, Dave, Eve Union completed successfully.
▼ Hint
- Use
Union(), passing the second list as an argument. Union()automatically removes duplicates while combining both sequences.- Convert the result to a list with
ToList().
▼ Solution & Explanation
Explanation:
listA.Union(listB): Combines both sequences into one, automatically removing any duplicate values that appear in either list.- Result: Names like "Bob" and "Alice", which appear in both lists, are included only once in the final result.
- Order:
Union()preserves the order elements were first encountered, scanninglistAfirst and thenlistBfor any new values. .ToList(): Converts the resulting sequence into a concreteList<string>.
Exercise 38: Finding the Most Frequent Number
Practice Problem: Find the most frequently occurring integer in an array.
Purpose: This exercise helps you practice combining GroupBy(), OrderByDescending(), and First() to identify the group with the highest count.
Given Input: numbers = new int[] { 1, 3, 2, 3, 4, 3, 2, 5, 3 }
Expected Output:
Finding the most frequently occurring number: Most Frequent Number: 3 Search completed successfully.
▼ Hint
- Use
GroupBy()with a lambda that groups each number by itself:n => n. - Sort the resulting groups by
group.Count()in descending order. - Take the first group with
First(), then access itsKeyproperty to get the actual number.
▼ Solution & Explanation
Explanation:
numbers.GroupBy(n => n): Groups every occurrence of each number together..OrderByDescending(group => group.Count()): Sorts the groups by how many elements they each contain, largest group first..First(): Selects the group with the highest count, the most frequent number..Key: Extracts the actual number value from the winning group, sinceKeyholds the value that was grouped on.
Exercise 39: Finding the Highest Paid Employee per Department
Practice Problem: Group a list of Employee objects by Department and find the highest-paid employee in each department.
Purpose: This exercise helps you practice combining GroupBy() with MaxBy() to find the top element within each individual group, rather than across the whole collection.
Given Input: Alice (Engineering, $8500), Bob (Sales, $6000), Charlie (Engineering, $9200), Dave (Sales, $7200), Eve (Marketing, $5500)
Expected Output:
Finding the highest-paid employee in each department: Engineering: Charlie ($9200) Sales: Dave ($7200) Marketing: Eve ($5500) Search completed successfully.
▼ Hint
- Use
GroupBy()to group employees by their Department. - Inside a
Select(), callgroup.MaxBy(e => e.Salary)to find the top earner in each group. - The result is one Employee per department, the highest-paid one in that group.
▼ Solution & Explanation
Explanation:
employees.GroupBy(e => e.Department): Groups all employees by which department they belong to.group.MaxBy(e => e.Salary): Within each department's group, finds the single Employee object with the highest Salary..Select(group => ...): Applies this max-finding logic to every department group, producing one result per department.- Why
MaxBy(): Because it returns the whole Employee object rather than just the salary value, both the employee's name and salary remain available afterward.
Exercise 40: Splitting a List into Chunks
Practice Problem: Given a large list of integers, split it into smaller sub-lists (chunks) of exactly 5 elements each, using the Chunk operator.
Purpose: This exercise helps you practice using Chunk() to break a large sequence into fixed-size batches, useful for processing large datasets in manageable pieces.
Given Input: A list of integers from 1 to 12
Expected Output:
Splitting a list of 12 numbers into chunks of 5: Chunk 1: 1, 2, 3, 4, 5 Chunk 2: 6, 7, 8, 9, 10 Chunk 3: 11, 12 Chunking completed successfully.
▼ Hint
- Use the
Chunk(5)method directly on the collection to split it into groups of 5. - Each chunk is returned as its own array; the last chunk may contain fewer elements if the total isn't evenly divisible.
- Loop through the chunks and print each one's contents.
▼ Solution & Explanation
Explanation:
numbers.Chunk(5): Splits the sequence into consecutive groups of up to 5 elements each, returning a sequence of arrays.- The final chunk: Contains whatever elements remain, which may be fewer than 5 if the total count doesn't divide evenly, as seen with the last chunk here containing only 2 numbers.
.ToArray(): Converts the sequence of chunks into an array of arrays for easy indexed access.- Why this matters:
Chunk()is especially useful for batch processing large datasets in manageable, fixed-size groups.

Leave a Reply