PYnative

Python Programming

  • Learn Python ▼
    • Python Tutorials
    • Python Basics
    • Python Interview Q&As
  • Exercises ▼
    • Python Exercises
    • C++ Exercises
    • C Programming Exercises
    • Java Exercises
    • C# Exercises
  • Quizzes
  • Code Editor ▼
    • Online Python Code Editor
    • Online C Compiler
    • Online C++ Compiler
    • Online Java Compiler
    • Online C# Compiler
Home » C# Exercises » C# List Exercises: 35 Coding Problems with Solutions

C# List Exercises: 35 Coding Problems with Solutions

Updated on: July 15, 2026 | Leave a Comment

List<T> is the workhorse collection in C#, a resizable array that adapts to your data instead of forcing you to know its size up front.

This collection of 35 C# List exercises covers everything from basic adding and removing to sorting, searching, filtering, and building small interactive programs powered entirely by lists.

Each exercise includes a Practice Problem, Purpose, Hint, and a full Solution with Explanation, so you build a strong, practical understanding of List<T> in real code.

Also, See: C# Exercises with  22 topic-wise sets and 620+ practice questions.

What You’ll Practice

  • Fundamentals: Creating, adding, removing, and counting list items.
  • Searching & Sorting: Contains(), IndexOf(), Sort(), and custom comparisons.
  • Manipulation: Removing duplicates, reversing, and filtering lists.
  • Practical Use: Building small menu-driven, list-backed programs.
+ Table Of Contents (35 Exercises)

Table of contents

  • Exercise 1: Initialize and Print
  • Exercise 2: Add & Insert
  • Exercise 3: Remove Elements
  • Exercise 4: Check Existence
  • Exercise 5: Count and Clear
  • Exercise 6: Find Index
  • Exercise 7: Sum and Average
  • Exercise 8: Min and Max
  • Exercise 9: List to Array
  • Exercise 10: Reverse a List
  • Exercise 11: Filter Even Numbers
  • Exercise 12: Remove Duplicates
  • Exercise 13: Sort Lists
  • Exercise 14: Merge Two Lists
  • Exercise 15: Find All Matches
  • Exercise 16: Insert Range
  • Exercise 17: Check for Substring
  • Exercise 18: Update Elements
  • Exercise 19: Split a List
  • Exercise 20: List of Custom Objects
  • Exercise 21: Forward and Backward Traversal
  • Exercise 22: First and Last Manipulation
  • Exercise 23: Mid-List Insertion
  • Exercise 24: Targeted Node Removal
  • Exercise 25: Convert to Standard List
  • Exercise 26: LINQ Filtering & Sorting
  • Exercise 27: Group By
  • Exercise 28: Custom Object Sorting
  • Exercise 29: Chunking a List
  • Exercise 30: Flattening Lists
  • Exercise 31: Binary Search
  • Exercise 32: Dictionary Conversion
  • Exercise 33: Check Conditions (All/Any)
  • Exercise 34: Rotate Elements
  • Exercise 35: Frequency Count

Exercise 1: Initialize and Print

Practice Problem: Create a list of strings containing 5 fruit names. Print each fruit on a new line using a foreach loop.

Purpose: This exercise helps you practice the most basic list workflow: initializing a List<T> with values right away, and iterating over it with foreach.

Given Input: List<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date", "Elderberry" };

Expected Output:

Apple
Banana
Cherry
Date
Elderberry
▼ Hint
  • Use a collection initializer to fill the list with all 5 names at once.
  • Use a foreach (string fruit in fruits) loop to visit each element.
  • Print each fruit with its own Console.WriteLine call.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date", "Elderberry" };

        foreach (string fruit in fruits)
        {
            Console.WriteLine(fruit);
        }
    }
}Code language: C# (cs)

Explanation:

  • List<string> fruits = new List<string> { ... }: Creates and populates the list in a single statement using a collection initializer.
  • foreach (string fruit in fruits): Visits every element in the list, one at a time, in the order they were added.
  • Console.WriteLine(fruit): Prints the current fruit before moving on to the next iteration.

Exercise 2: Add & Insert

Practice Problem: Create an empty list of integers. Add the numbers 10, 20, and 30 using .Add(). Then, insert the number 15 at index 1.

Purpose: This exercise helps you practice building up a list one element at a time with Add(), and shows how Insert() places a value at a specific position, shifting everything after it one slot to the right.

Given Input: numbers.Add(10); numbers.Add(20); numbers.Add(30); numbers.Insert(1, 15);

Expected Output: {10, 15, 20, 30}

▼ Hint
  • Start with new List<int>(), an empty list.
  • Call .Add() three times to append 10, 20, and 30 in order.
  • Call .Insert(1, 15) to place 15 at index 1, pushing 20 and 30 one position later.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>();
        numbers.Add(10);
        numbers.Add(20);
        numbers.Add(30);

        numbers.Insert(1, 15);

        Console.WriteLine("{" + string.Join(", ", numbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • new List<int>(): Creates a list with no elements, ready to be built up with Add() calls.
  • numbers.Add(10): Appends a value to the end of the list, growing it by one element each time.
  • numbers.Insert(1, 15): Places 15 at index 1, shifting the existing elements at that position and after it one slot later.

Exercise 3: Remove Elements

Practice Problem: Create a list of integers from 1 to 10. Remove the number 5 by value, and remove the element at index 0.

Purpose: This exercise helps you practice the difference between Remove(), which deletes the first matching value, and RemoveAt(), which deletes whatever happens to be at a given position.

Given Input: List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Expected Output: {2, 3, 4, 6, 7, 8, 9, 10}

▼ Hint
  • Call numbers.Remove(5) to delete the value 5 wherever it appears in the list.
  • Call numbers.RemoveAt(0) to delete whatever element is currently first in the list.
  • Do the value removal first, then the index removal, so the index refers to the list’s state after the first removal.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        numbers.Remove(5);
        numbers.RemoveAt(0);

        Console.WriteLine("{" + string.Join(", ", numbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • numbers.Remove(5): Searches for the value 5 and removes the first occurrence of it, regardless of where it sits in the list.
  • numbers.RemoveAt(0): Removes whatever element is currently at index 0, which by this point is the value 1.
  • Remaining list: Every element automatically shifts down to fill the gap left by each removal, so no manual re-indexing is needed.

Exercise 4: Check Existence

Practice Problem: Create a list of colors. Ask the user for a color, and use .Contains() to check if it exists in the list.

Purpose: This exercise helps you practice .Contains() for a simple existence check, without needing to loop through the list or track an index yourself.

Given Input: List<string> colors = new List<string> { "Red", "Green", "Blue", "Yellow" };, checking for "Blue" and "Purple" in place of real console input.

Expected Output:

Blue is in the list.
Purple is not in the list.
▼ Hint
  • In a real console app, you would capture the color name with Console.ReadLine(); here a fixed value stands in for that input so the output stays predictable.
  • Call colors.Contains(input), which returns true or false.
  • Use a ternary expression or an if/else to print a different message depending on the result.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> colors = new List<string> { "Red", "Green", "Blue", "Yellow" };

        // In a real console app, replace these with:
        // string userInput = Console.ReadLine();
        string firstCheck = "Blue";
        string secondCheck = "Purple";

        Console.WriteLine(firstCheck + (colors.Contains(firstCheck) ? " is in the list." : " is not in the list."));
        Console.WriteLine(secondCheck + (colors.Contains(secondCheck) ? " is in the list." : " is not in the list."));
    }
}Code language: C# (cs)

Explanation:

  • colors.Contains(firstCheck): Scans the list and returns true as soon as a matching value is found.
  • Ternary expression: Picks between two different message strings based on the boolean result of Contains().
  • secondCheck: Demonstrates the “not found” branch, since "Purple" was never added to the list.

Exercise 5: Count and Clear

Practice Problem: Create a list of double values. Print the total count of elements using .Count, clear the list using .Clear(), and verify its count is now 0.

Purpose: This exercise helps you practice the Count property and the Clear() method, useful whenever you need to empty a list without replacing it with a brand new instance.

Given Input: List<double> values = new List<double> { 1.1, 2.2, 3.3, 4.4 };

Expected Output:

Count before clear: 4
Count after clear: 0
▼ Hint

Read values.Count once before calling Clear(), and once again afterward, to see the difference.

▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<double> values = new List<double> { 1.1, 2.2, 3.3, 4.4 };

        Console.WriteLine("Count before clear: " + values.Count);

        values.Clear();

        Console.WriteLine("Count after clear: " + values.Count);
    }
}Code language: C# (cs)

Explanation:

  • values.Count: Reports how many elements are currently stored in the list.
  • values.Clear(): Removes every element from the list in one call, leaving the same list instance empty rather than replacing it.
  • Second Console.WriteLine: Confirms the list is now empty by reading Count again after clearing.

Exercise 6: Find Index

Practice Problem: Create a list of names. Find and print the index of a specific name using .IndexOf(). Return -1 if it doesn’t exist.

Purpose: This exercise helps you practice .IndexOf(), which already returns -1 by convention when the value isn’t found, so there’s no need to write your own separate check for a missing element.

Given Input: List<string> names = new List<string> { "Alice", "Bob", "Charlie", "Diana" };, looking up "Charlie" and "Zoe".

Expected Output:

Index of Charlie: 2
Index of Zoe: -1
▼ Hint
  • Call names.IndexOf("Charlie") to get the zero-based position of that name.
  • Call names.IndexOf("Zoe") for a name that was never added.
  • No extra logic is required for the missing case, IndexOf() already returns -1 on its own.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> names = new List<string> { "Alice", "Bob", "Charlie", "Diana" };

        Console.WriteLine("Index of Charlie: " + names.IndexOf("Charlie"));
        Console.WriteLine("Index of Zoe: " + names.IndexOf("Zoe"));
    }
}Code language: C# (cs)

Explanation:

  • names.IndexOf("Charlie"): Returns 2, since "Charlie" is the third element at zero-based index 2.
  • names.IndexOf("Zoe"): Returns -1 automatically, since "Zoe" was never added to the list.

Exercise 7: Sum and Average

Practice Problem: Create a list of integers. Calculate and print their sum and average without using LINQ, using a loop instead.

Purpose: This exercise helps you practice manually accumulating a total with a loop, and reinforces why one of the two numbers involved in a division needs to be cast to a floating-point type to get a decimal result.

Given Input: List<int> numbers = new List<int> { 4, 8, 15, 16, 23, 42 };

Expected Output:

Sum = 108
Average = 18
▼ Hint
  • Initialize a running total to 0 before the loop.
  • Use a foreach loop to add each number to the running total.
  • Cast the sum to double before dividing by numbers.Count, so the average isn’t accidentally truncated by integer division.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 4, 8, 15, 16, 23, 42 };

        int sum = 0;
        foreach (int number in numbers)
        {
            sum += number;
        }

        double average = (double)sum / numbers.Count;

        Console.WriteLine("Sum = " + sum);
        Console.WriteLine("Average = " + average);
    }
}Code language: C# (cs)

Explanation:

  • int sum = 0: Starts the running total at zero before any numbers have been added.
  • sum += number: Adds each element to the total as the loop visits it.
  • (double)sum / numbers.Count: Casts the sum to double first, so the division produces a decimal result instead of being rounded down as integer division would.

Exercise 8: Min and Max

Practice Problem: Write a program to find the maximum and minimum values in a list of integers using a for loop.

Purpose: This exercise helps you practice the classic running-maximum and running-minimum pattern, tracking the best value seen so far while scanning through a list by index.

Given Input: List<int> numbers = new List<int> { 34, 12, 89, 5, 67, 23 };

Expected Output:

Max = 89
Min = 5
▼ Hint
  • Start both max and min at numbers[0] before the loop begins.
  • Loop from index 1 to the end of the list with a for loop.
  • Update max whenever you find a larger value, and update min whenever you find a smaller one.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 34, 12, 89, 5, 67, 23 };

        int max = numbers[0];
        int min = numbers[0];

        for (int i = 1; i < numbers.Count; i++)
        {
            if (numbers[i] > max)
                max = numbers[i];

            if (numbers[i] < min)
                min = numbers[i];
        }

        Console.WriteLine("Max = " + max);
        Console.WriteLine("Min = " + min);
    }
}Code language: C# (cs)

Explanation:

  • int max = numbers[0]; int min = numbers[0];: Seeds both trackers with the first element, giving them a valid starting point to compare against.
  • for (int i = 1; i < numbers.Count; i++): Starts at index 1 since index 0 was already used to seed max and min.
  • if (numbers[i] > max) max = numbers[i];: Updates the running maximum whenever a larger value is found.
  • if (numbers[i] < min) min = numbers[i];: Updates the running minimum whenever a smaller value is found.

Exercise 9: List to Array

Practice Problem: Create a list of strings, convert it into a standard array using .ToArray(), and print the array elements.

Purpose: This exercise helps you practice converting between a resizable List<T> and a fixed-size array, which is sometimes required when calling an older API that expects an array parameter.

Given Input: List<string> fruits = new List<string> { "Apple", "Banana", "Cherry" };

Expected Output:

Apple
Banana
Cherry
▼ Hint
  • Call .ToArray() on the list to produce a new string[].
  • The resulting array is a completely separate copy, not a view into the original list.
  • Loop through the array the same way you would loop through a list, with foreach.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> fruits = new List<string> { "Apple", "Banana", "Cherry" };

        string[] fruitArray = fruits.ToArray();

        foreach (string fruit in fruitArray)
        {
            Console.WriteLine(fruit);
        }
    }
}Code language: C# (cs)

Explanation:

  • fruits.ToArray(): Copies every element from the list into a brand new, fixed-size array.
  • string[] fruitArray: Declares the result as a standard array type, distinct from the original List<string>.
  • foreach (string fruit in fruitArray): Iterates over the array exactly the same way as it would over the original list.

Exercise 10: Reverse a List

Practice Problem: Create a list of characters (A to E) and reverse the order of elements using the .Reverse() method.

Purpose: This exercise helps you practice .Reverse(), which flips the order of a list’s elements in place, without needing to build a second list or loop backward manually.

Given Input: List<char> letters = new List<char> { 'A', 'B', 'C', 'D', 'E' };

Expected Output: {E, D, C, B, A}

▼ Hint
  • Call letters.Reverse() directly on the list, with no arguments.
  • This method reverses the list in place, it does not return a new list.
  • Print the list afterward to see the new order.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<char> letters = new List<char> { 'A', 'B', 'C', 'D', 'E' };

        letters.Reverse();

        Console.WriteLine("{" + string.Join(", ", letters) + "}");
    }
}Code language: C# (cs)

Explanation:

  • letters.Reverse(): Flips the order of every element in the list directly, without allocating a second list.
  • In-place modification: The original letters list itself is changed, so no reassignment like letters = letters.Reverse() is needed or even valid here.
  • string.Join(", ", letters): Combines the now-reversed elements into a single readable line for display.

Exercise 11: Filter Even Numbers

Practice Problem: Given a list of integers, create a new list that contains only the even numbers from the original list.

Purpose: This exercise helps you practice building a filtered list by hand with a loop and the modulo operator, the same underlying idea LINQ’s Where() uses internally.

Given Input: List<int> numbers = new List<int> { 12, 7, 8, 3, 4, 15, 22, 9 };

Expected Output: {12, 8, 4, 22}

▼ Hint
  • Create an empty result list before the loop starts.
  • Loop through the original list with foreach.
  • Use number % 2 == 0 to test whether the current number is even.
  • Add matching numbers to the result list as you go.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class ListFilter
{
    public static List<int> FilterEven(List<int> numbers)
    {
        List<int> evens = new List<int>();

        foreach (int number in numbers)
        {
            if (number % 2 == 0)
                evens.Add(number);
        }

        return evens;
    }
}

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 12, 7, 8, 3, 4, 15, 22, 9 };
        List<int> evenNumbers = ListFilter.FilterEven(numbers);

        Console.WriteLine("{" + string.Join(", ", evenNumbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • List<int> evens = new List<int>(): Starts a fresh, empty list to collect only the numbers that pass the even check.
  • number % 2 == 0: Tests whether dividing by 2 leaves no remainder, which is the standard way to check for an even number.
  • evens.Add(number): Appends the current number to the result list only when it passes the even check.

Exercise 12: Remove Duplicates

Practice Problem: Write a program that takes a list with duplicate integers and removes them, keeping only unique values, without using HashSet.

Purpose: This exercise helps you practice building a unique list manually with .Contains(), and shows the trade-off of this approach: it is simple to write but slower on large lists than a HashSet-based solution would be.

Given Input: List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 4, 5, 1 };

Expected Output: {1, 2, 3, 4, 5}

▼ Hint
  • Create an empty result list to hold only unique values.
  • Loop through the original list with foreach.
  • Before adding a number, check !unique.Contains(number) to make sure it hasn’t already been added.
  • Only add the number to the result list if that check passes.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class DuplicateRemover
{
    public static List<int> RemoveDuplicates(List<int> numbers)
    {
        List<int> unique = new List<int>();

        foreach (int number in numbers)
        {
            if (!unique.Contains(number))
                unique.Add(number);
        }

        return unique;
    }
}

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 4, 5, 1 };
        List<int> uniqueNumbers = DuplicateRemover.RemoveDuplicates(numbers);

        Console.WriteLine("{" + string.Join(", ", uniqueNumbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • !unique.Contains(number): Checks whether this exact value has already been placed into the result list before adding it again.
  • Order preserved: Since values are added in the order they are first seen, the result keeps the original list’s ordering rather than sorting or scrambling it.
  • Without HashSet: Each Contains() call scans the growing result list directly, which is simple to reason about but does more work than a hash-based lookup would.

Exercise 13: Sort Lists

Practice Problem: Create a list of random integers. Sort it in ascending order, print it, and then sort it in descending order.

Purpose: This exercise helps you practice the default .Sort() method for ascending order, and shows how passing a custom comparison delegate flips the sort order to descending without writing a separate sorting algorithm.

Given Input: List<int> numbers = new List<int> { 42, 17, 8, 99, 23, 4 };

Expected Output:

Ascending: {4, 8, 17, 23, 42, 99}
Descending: {99, 42, 23, 17, 8, 4}
▼ Hint
  • Call numbers.Sort() with no arguments to sort in ascending order.
  • Call numbers.Sort((a, b) => b.CompareTo(a)) to sort the same list in descending order.
  • Both calls sort the list in place, so print it again after each call to see the new order.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 42, 17, 8, 99, 23, 4 };

        numbers.Sort();
        Console.WriteLine("Ascending: {" + string.Join(", ", numbers) + "}");

        numbers.Sort((a, b) => b.CompareTo(a));
        Console.WriteLine("Descending: {" + string.Join(", ", numbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • numbers.Sort(): Sorts the list in place using the default ascending comparison for integers.
  • numbers.Sort((a, b) => b.CompareTo(a)): Supplies a custom comparison that reverses the usual result, producing a descending order instead.
  • In-place sorting: Both calls modify the same numbers list directly, so the second sort completely overwrites the ordering left by the first.

Exercise 14: Merge Two Lists

Practice Problem: Create two separate lists of strings. Combine them into a third list, ensuring no duplicate strings are added.

Purpose: This exercise helps you practice combining two lists while guarding against duplicates, similar to the duplicate-removal pattern but applied across two separate sources instead of one.

Given Input: listA = { "Apple", "Banana", "Cherry" } and listB = { "Banana", "Date", "Cherry", "Fig" }

Expected Output: Apple, Banana, Cherry, Date, Fig

▼ Hint
  • Start the merged list as a copy of the first list, using the List<string> constructor.
  • Loop through the second list, checking !merged.Contains(item) before adding each one.
  • This way, entries that appear in both lists only end up in the merged result once.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class ListMerger
{
    public static List<string> MergeUnique(List<string> listA, List<string> listB)
    {
        List<string> merged = new List<string>(listA);

        foreach (string item in listB)
        {
            if (!merged.Contains(item))
                merged.Add(item);
        }

        return merged;
    }
}

class Program
{
    static void Main()
    {
        List<string> listA = new List<string> { "Apple", "Banana", "Cherry" };
        List<string> listB = new List<string> { "Banana", "Date", "Cherry", "Fig" };

        List<string> merged = ListMerger.MergeUnique(listA, listB);

        Console.WriteLine(string.Join(", ", merged));
    }
}Code language: C# (cs)

Explanation:

  • new List<string>(listA): Creates an independent copy of the first list, so the original listA is never modified by the merge.
  • !merged.Contains(item): Skips any string from the second list that already exists in the merged result, whether it came from the first list or an earlier item in the second.
  • Result order: Keeps every item from listA first, followed by only the new, non-duplicate items from listB.

Exercise 15: Find All Matches

Practice Problem: Use .FindAll() to extract all words longer than 5 characters from a list of strings.

Purpose: This exercise helps you practice .FindAll(), a built-in List<T> method that takes a predicate delegate and returns every matching element as a new list, without writing a manual loop.

Given Input: List<string> words = new List<string> { "cat", "elephant", "dog", "giraffe", "ant", "butterfly" };

Expected Output: elephant, giraffe, butterfly

▼ Hint
  • Call words.FindAll(word => word.Length > 5) directly on the list.
  • The predicate lambda should return true for exactly the words you want to keep.
  • The result is already a List<string>, ready to print or use directly.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> words = new List<string> { "cat", "elephant", "dog", "giraffe", "ant", "butterfly" };

        List<string> longWords = words.FindAll(word => word.Length > 5);

        Console.WriteLine(string.Join(", ", longWords));
    }
}Code language: C# (cs)

Explanation:

  • words.FindAll(word => word.Length > 5): Runs the predicate against every word and collects only the ones where it returns true.
  • word.Length > 5: The actual condition being tested, keeping only words with more than 5 characters.
  • longWords: A brand new list containing just the matches, leaving the original words list untouched.

Exercise 16: Insert Range

Practice Problem: Create a list of numbers (1, 2, 6, 7). Create a second list (3, 4, 5). Use .InsertRange() to insert the second list into the middle of the first list so it becomes sequential.

Purpose: This exercise helps you practice .InsertRange(), which inserts an entire collection at a specific position in one call, instead of looping and calling .Insert() once per element.

Given Input: primary = { 1, 2, 6, 7 } and toInsert = { 3, 4, 5 }

Expected Output: {1, 2, 3, 4, 5, 6, 7}

▼ Hint
  • Figure out which index in primary the gap sits at, right between 2 and 6.
  • Call primary.InsertRange(index, toInsert) at that position.
  • Every element in toInsert gets inserted in order, and everything after the insertion point shifts to make room.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> primary = new List<int> { 1, 2, 6, 7 };
        List<int> toInsert = new List<int> { 3, 4, 5 };

        primary.InsertRange(2, toInsert);

        Console.WriteLine("{" + string.Join(", ", primary) + "}");
    }
}Code language: C# (cs)

Explanation:

  • primary.InsertRange(2, toInsert): Inserts every element of toInsert starting at index 2, which is exactly where the value 6 currently sits.
  • Shifted elements: The values 6 and 7 automatically move later in the list to make room for the three newly inserted numbers.
  • Order preserved: The inserted numbers 3, 4, and 5 keep their original relative order from toInsert.

Exercise 17: Check for Substring

Practice Problem: Given a list of book titles, filter and print only the titles that contain the word “The”.

Purpose: This exercise helps you practice combining .FindAll() with a string method like .Contains(), applying the same filtering pattern from Exercise 15 to a text-matching condition instead of a length check.

Given Input: { "The Great Gatsby", "Moby Dick", "The Catcher in the Rye", "1984", "The Hobbit" }

Expected Output:

The Great Gatsby
The Catcher in the Rye
The Hobbit
▼ Hint
  • Call titles.FindAll(title => title.Contains("The")).
  • Remember that .Contains() on a string is case-sensitive by default, so lowercase “the” inside a title won’t count as a match on its own.
  • Print each matching title on its own line with a foreach loop.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> titles = new List<string> { "The Great Gatsby", "Moby Dick", "The Catcher in the Rye", "1984", "The Hobbit" };

        List<string> matches = titles.FindAll(title => title.Contains("The"));

        foreach (string title in matches)
        {
            Console.WriteLine(title);
        }
    }
}Code language: C# (cs)

Explanation:

  • title.Contains("The"): Checks whether the exact substring “The” appears anywhere in the title, including at the very start.
  • "Moby Dick" and "1984": Correctly excluded, since neither title contains the substring “The” at all.
  • "The Catcher in the Rye": Still matches, because it contains “The” with a capital T at the start, even though it also contains a lowercase “the” later on.

Exercise 18: Update Elements

Practice Problem: Write a program that iterates through a list of integers and multiplies every odd number by 2.

Purpose: This exercise helps you practice modifying a list’s elements in place, and shows why a for loop with an index is needed here instead of foreach, since the iteration variable in a foreach loop cannot be assigned back into the list.

Given Input: List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

Expected Output: {2, 2, 6, 4, 10, 6}

▼ Hint
  • Use a for loop with an index variable, rather than foreach, since you need to write a new value back into the list.
  • Check numbers[i] % 2 != 0 to test whether the current element is odd.
  • If it is, multiply it by 2 and assign the result back with numbers[i] *= 2.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

        for (int i = 0; i < numbers.Count; i++)
        {
            if (numbers[i] % 2 != 0)
                numbers[i] *= 2;
        }

        Console.WriteLine("{" + string.Join(", ", numbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • for (int i = 0; i < numbers.Count; i++): Walks through every position in the list by index, which is required since the elements themselves need to be replaced.
  • numbers[i] % 2 != 0: Tests whether the value at the current position is odd.
  • numbers[i] *= 2: Doubles the value in place at that index, directly modifying the original list rather than building a new one.

Exercise 19: Split a List

Practice Problem: Take a list of 10 numbers and split it into two separate lists: one for the first 5 elements and one for the last 5 elements.

Purpose: This exercise helps you practice .GetRange(), which extracts a contiguous slice of a list into a brand new list, without needing to loop and copy elements manually.

Given Input: List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Expected Output:

First half: {1, 2, 3, 4, 5}
Second half: {6, 7, 8, 9, 10}
▼ Hint
  • Call numbers.GetRange(0, 5) to grab the first 5 elements, starting at index 0.
  • Call numbers.GetRange(5, 5) to grab the next 5 elements, starting at index 5.
  • The second argument to GetRange() is always a count, not an ending index.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        List<int> firstHalf = numbers.GetRange(0, 5);
        List<int> secondHalf = numbers.GetRange(5, 5);

        Console.WriteLine("First half: {" + string.Join(", ", firstHalf) + "}");
        Console.WriteLine("Second half: {" + string.Join(", ", secondHalf) + "}");
    }
}Code language: C# (cs)

Explanation:

  • numbers.GetRange(0, 5): Copies 5 elements starting at index 0 into a brand new list.
  • numbers.GetRange(5, 5): Copies the next 5 elements, starting at index 5, into a second new list.
  • Original numbers list: Remains completely unchanged, since GetRange() always returns a separate copy.

Exercise 20: List of Custom Objects

Practice Problem: Create a Student class with properties Id and Name. Create a List<Student>, add 3 students, and print their details by iterating through the list.

Purpose: This exercise helps you practice storing your own custom class inside a List<T>, rather than a built-in type like int or string, and accessing each object’s properties while iterating.

Given Input: Three students with Ids 1, 2, and 3, named Alice, Bob, and Charlie.

Expected Output:

Id: 1, Name: Alice
Id: 2, Name: Bob
Id: 3, Name: Charlie
▼ Hint
  • Give the Student class two auto-implemented properties, Id and Name.
  • Use object initializer syntax, new Student { Id = 1, Name = "Alice" }, to create each student.
  • Loop through the List<Student> with foreach, reading .Id and .Name off each element.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Id = 1, Name = "Alice" },
            new Student { Id = 2, Name = "Bob" },
            new Student { Id = 3, Name = "Charlie" }
        };

        foreach (Student student in students)
        {
            Console.WriteLine("Id: " + student.Id + ", Name: " + student.Name);
        }
    }
}Code language: C# (cs)

Explanation:

  • public int Id { get; set; }: An auto-implemented property, giving Student a simple readable and writable field without a manually written backing field.
  • new Student { Id = 1, Name = "Alice" }: Creates a fully populated Student object in a single expression using object initializer syntax.
  • foreach (Student student in students): Iterates over the list of custom objects exactly like it would over a list of any built-in type.
  • student.Id and student.Name: Reads each property directly off the current Student object for display.

Exercise 21: Forward and Backward Traversal

Practice Problem: Create a LinkedList<string> of steps in a workflow. Print the list from first to last using .Next, and then from last to first using .Previous.

Purpose: This exercise helps you practice manually walking a LinkedList<T> node by node, using its First and Last properties as starting points and .Next or .Previous to move along the chain.

Given Input: A workflow with the steps Design, Develop, Test, and Deploy added in that order.

Expected Output:

Forward: Design -> Develop -> Test -> Deploy
Backward: Deploy -> Test -> Develop -> Design
▼ Hint
  • Start a node reference at workflow.First for the forward pass.
  • Loop with a while (current != null) condition, moving to current.Next each time.
  • Repeat the same idea for the backward pass, starting at workflow.Last and moving through current.Previous instead.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Text;

class Program
{
    static void Main()
    {
        LinkedList<string> workflow = new LinkedList<string>();
        workflow.AddLast("Design");
        workflow.AddLast("Develop");
        workflow.AddLast("Test");
        workflow.AddLast("Deploy");

        StringBuilder forward = new StringBuilder();
        LinkedListNode<string> current = workflow.First;
        while (current != null)
        {
            forward.Append(current.Value);
            if (current.Next != null)
                forward.Append(" -> ");
            current = current.Next;
        }
        Console.WriteLine("Forward: " + forward);

        StringBuilder backward = new StringBuilder();
        LinkedListNode<string> currentBack = workflow.Last;
        while (currentBack != null)
        {
            backward.Append(currentBack.Value);
            if (currentBack.Previous != null)
                backward.Append(" -> ");
            currentBack = currentBack.Previous;
        }
        Console.WriteLine("Backward: " + backward);
    }
}Code language: C# (cs)

Explanation:

  • workflow.First and workflow.Last: Give direct access to the first and last nodes of the linked list without needing to traverse to find them.
  • current.Next: Moves the reference to the following node, or to null once the end of the list is reached.
  • currentBack.Previous: Moves the reference backward toward the beginning of the list, the mirror image of .Next.

Exercise 22: First and Last Manipulation

Practice Problem: Create a list of tasks. Use .AddFirst() to insert high-priority tasks and .AddLast() for low-priority tasks. Remove them using .RemoveFirst() and .RemoveLast().

Purpose: This exercise helps you practice LinkedList<T>‘s constant-time operations at both ends, useful whenever new items need to jump to the front of a queue-like structure instead of always going to the back.

Given Input: tasks.AddLast("Write report"); tasks.AddFirst("Fix critical bug"); tasks.AddLast("Update docs");

Expected Output:

Tasks: Fix critical bug, Write report, Update docs
After removing first and last: Write report
▼ Hint
  • Use LinkedList<string>, since it is the collection that actually offers AddFirst(), AddLast(), RemoveFirst(), and RemoveLast().
  • Call .AddFirst() for anything urgent, pushing it ahead of everything already queued.
  • Call .RemoveFirst() and .RemoveLast() afterward to drop whichever tasks currently sit at each end.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        LinkedList<string> tasks = new LinkedList<string>();
        tasks.AddLast("Write report");
        tasks.AddFirst("Fix critical bug");
        tasks.AddLast("Update docs");

        Console.WriteLine("Tasks: " + string.Join(", ", tasks));

        tasks.RemoveFirst();
        tasks.RemoveLast();

        Console.WriteLine("After removing first and last: " + string.Join(", ", tasks));
    }
}Code language: C# (cs)

Explanation:

  • tasks.AddFirst("Fix critical bug"): Places the urgent task at the very front, ahead of the task already added with AddLast().
  • tasks.RemoveFirst(): Removes whatever is currently at the front of the list, here the high-priority bug fix.
  • tasks.RemoveLast(): Removes whatever is currently at the back of the list, here the low-priority documentation update.

Exercise 23: Mid-List Insertion

Practice Problem: Create a linked list of numbers (1 -> 2 -> 4). Find the node containing 2 using .Find(), and use .AddAfter() to insert the missing 3.

Purpose: This exercise helps you practice locating a specific node with .Find(), then using that exact node reference with .AddAfter() to insert a new value at a precise position.

Given Input: numbers.AddLast(1); numbers.AddLast(2); numbers.AddLast(4);

Expected Output: {1, 2, 3, 4}

▼ Hint
  • Call numbers.Find(2) to get the LinkedListNode<int> that holds the value 2.
  • Pass that node into numbers.AddAfter(node, 3).
  • The new value 3 is inserted directly after the found node, without disturbing anything before it.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        LinkedList<int> numbers = new LinkedList<int>();
        numbers.AddLast(1);
        numbers.AddLast(2);
        numbers.AddLast(4);

        LinkedListNode<int> node = numbers.Find(2);
        numbers.AddAfter(node, 3);

        Console.WriteLine("{" + string.Join(", ", numbers) + "}");
    }
}Code language: C# (cs)

Explanation:

  • numbers.Find(2): Scans the list looking for the first node whose value equals 2, returning a direct reference to that node.
  • numbers.AddAfter(node, 3): Inserts a brand new node holding 3 immediately following the found node.
  • Resulting order: The list becomes 1, 2, 3, 4, exactly filling the gap that was originally missing.

Exercise 24: Targeted Node Removal

Practice Problem: Given a linked list of songs, find a specific song node using .Find() and remove it directly by passing the node object into .Remove().

Purpose: This exercise helps you practice the node-based overload of .Remove(), which deletes an exact, already-located node directly rather than searching the list again by value.

Given Input: A playlist containing “Bohemian Rhapsody”, “Stairway to Heaven”, “Hotel California”, and “Imagine”.

Expected Output: Bohemian Rhapsody, Stairway to Heaven, Imagine

▼ Hint
  • Call songs.Find("Hotel California") to get a reference to that specific node.
  • Check that the result isn’t null before removing, in case the song wasn’t in the list at all.
  • Pass the node itself into songs.Remove(songNode), rather than passing the string value again.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        LinkedList<string> songs = new LinkedList<string>();
        songs.AddLast("Bohemian Rhapsody");
        songs.AddLast("Stairway to Heaven");
        songs.AddLast("Hotel California");
        songs.AddLast("Imagine");

        LinkedListNode<string> songNode = songs.Find("Hotel California");

        if (songNode != null)
            songs.Remove(songNode);

        Console.WriteLine(string.Join(", ", songs));
    }
}Code language: C# (cs)

Explanation:

  • songs.Find("Hotel California"): Locates the node holding that exact string, or returns null if no such song exists in the list.
  • if (songNode != null): Guards against calling Remove() on a node that was never found.
  • songs.Remove(songNode): Unlinks that exact node from the list directly, relying on the node reference rather than searching by value a second time.

Exercise 25: Convert to Standard List

Practice Problem: Create a LinkedList<int>. Write a method to copy all its elements into a standard List<int> without using built-in LINQ methods.

Purpose: This exercise helps you practice manually walking a linked list node by node to build an equivalent List<T>, reinforcing how a linked list’s node-based structure differs from a list’s index-based one.

Given Input: source.AddLast(10); source.AddLast(20); source.AddLast(30); source.AddLast(40);

Expected Output: {10, 20, 30, 40}

▼ Hint
  • Create an empty List<int> to hold the converted result.
  • Start a node reference at source.First.
  • Loop with while (current != null), adding current.Value to the list and moving to current.Next each time.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class LinkedListConverter
{
    public static List<int> ToStandardList(LinkedList<int> source)
    {
        List<int> result = new List<int>();

        LinkedListNode<int> current = source.First;
        while (current != null)
        {
            result.Add(current.Value);
            current = current.Next;
        }

        return result;
    }
}

class Program
{
    static void Main()
    {
        LinkedList<int> source = new LinkedList<int>();
        source.AddLast(10);
        source.AddLast(20);
        source.AddLast(30);
        source.AddLast(40);

        List<int> converted = LinkedListConverter.ToStandardList(source);

        Console.WriteLine("{" + string.Join(", ", converted) + "}");
    }
}Code language: C# (cs)

Explanation:

  • LinkedListNode<int> current = source.First: Starts the traversal at the first node in the linked list.
  • result.Add(current.Value): Copies the value out of the current node into the destination list.
  • current = current.Next: Advances to the following node, continuing until current becomes null at the end of the list.

Exercise 26: LINQ Filtering & Sorting

Practice Problem: Given a List<Product> with properties Name, Price, and Category, use LINQ to find all products in the “Electronics” category with a price greater than $500, sorted by price descending.

Purpose: This exercise helps you practice chaining Where() with multiple conditions and OrderByDescending() in a single LINQ pipeline, a very common combination when querying a list of custom objects.

Given Input: Five products across the Electronics and Furniture categories, with prices ranging from 150 to 1200.

Expected Output:

Laptop: 1200
Smartphone: 800
Monitor: 600
▼ Hint
  • Call Where(p => p.Category == "Electronics" && p.Price > 500) to apply both conditions at once.
  • Chain OrderByDescending(p => p.Price) onto the filtered result.
  • Loop through the final sequence with foreach to print each matching product.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

public class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
    public string Category { get; set; }
}

class Program
{
    static void Main()
    {
        List<Product> products = new List<Product>
        {
            new Product { Name = "Laptop", Price = 1200, Category = "Electronics" },
            new Product { Name = "Headphones", Price = 150, Category = "Electronics" },
            new Product { Name = "Smartphone", Price = 800, Category = "Electronics" },
            new Product { Name = "Desk", Price = 300, Category = "Furniture" },
            new Product { Name = "Monitor", Price = 600, Category = "Electronics" }
        };

        IOrderedEnumerable<Product> expensiveElectronics = products
            .Where(p => p.Category == "Electronics" && p.Price > 500)
            .OrderByDescending(p => p.Price);

        foreach (Product product in expensiveElectronics)
        {
            Console.WriteLine(product.Name + ": " + product.Price);
        }
    }
}Code language: C# (cs)

Explanation:

  • p.Category == "Electronics" && p.Price > 500: Combines both filtering rules into a single condition, so a product must satisfy both to pass through.
  • OrderByDescending(p => p.Price): Arranges the filtered products from most expensive to least expensive.
  • Method chaining: Where() and OrderByDescending() are linked directly, so filtering happens before sorting in a single readable pipeline.

Exercise 27: Group By

Practice Problem: Given a List<Employee> with properties Name, Department, and Salary, use LINQ to group employees by their Department and print the department names along with their average salary.

Purpose: This exercise helps you practice GroupBy() combined with an aggregate like Average() computed per group, rather than across the whole list at once.

Given Input: Five employees split across the Engineering, Sales, and Marketing departments.

Expected Output:

Engineering: 100000
Sales: 67500
Marketing: 60000
▼ Hint
  • Call employees.GroupBy(emp => emp.Department) to cluster employees by department.
  • Loop through the resulting groups, each of which exposes a Key (the department name).
  • Inside the loop, call group.Average(emp => emp.Salary) to compute that specific department’s average.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

public class Employee
{
    public string Name { get; set; }
    public string Department { get; set; }
    public double Salary { get; set; }
}

class Program
{
    static void Main()
    {
        List<Employee> employees = new List<Employee>
        {
            new Employee { Name = "Alice", Department = "Engineering", Salary = 95000 },
            new Employee { Name = "Bob", Department = "Sales", Salary = 65000 },
            new Employee { Name = "Charlie", Department = "Engineering", Salary = 105000 },
            new Employee { Name = "Diana", Department = "Sales", Salary = 70000 },
            new Employee { Name = "Eve", Department = "Marketing", Salary = 60000 }
        };

        IEnumerable<IGrouping<string, Employee>> departmentGroups = employees.GroupBy(emp => emp.Department);

        foreach (IGrouping<string, Employee> group in departmentGroups)
        {
            double averageSalary = group.Average(emp => emp.Salary);
            Console.WriteLine(group.Key + ": " + averageSalary);
        }
    }
}Code language: C# (cs)

Explanation:

  • GroupBy(emp => emp.Department): Clusters every employee into a group keyed by their department name.
  • IGrouping<string, Employee>: Represents one department’s group, exposing both its Key and the employees inside it.
  • group.Average(emp => emp.Salary): Computes the mean salary using only the employees within that specific group, not the entire company.

Exercise 28: Custom Object Sorting

Practice Problem: Create a List<Person> with Name and Age. Sort the list by Age using a custom IComparer<Person>.

Purpose: This exercise helps you practice implementing IComparer<T> as a standalone, reusable class, an alternative to passing a lambda directly into .Sort() when the comparison logic is worth naming and reusing elsewhere.

Given Input: Four people aged 30, 22, 45, and 28.

Expected Output:

Bob: 22
Diana: 28
Alice: 30
Charlie: 45
▼ Hint
  • Implement IComparer<Person> with a Compare(Person a, Person b) method.
  • Inside Compare(), delegate to a.Age.CompareTo(b.Age) rather than writing custom comparison logic by hand.
  • Pass a new instance of your comparer class into people.Sort(new AgeComparer()).
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class AgeComparer : IComparer<Person>
{
    public int Compare(Person a, Person b)
    {
        return a.Age.CompareTo(b.Age);
    }
}

class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 22 },
            new Person { Name = "Charlie", Age = 45 },
            new Person { Name = "Diana", Age = 28 }
        };

        people.Sort(new AgeComparer());

        foreach (Person person in people)
        {
            Console.WriteLine(person.Name + ": " + person.Age);
        }
    }
}Code language: C# (cs)

Explanation:

  • IComparer<Person>: Defines a reusable comparison rule as its own class, rather than a one-off inline lambda.
  • a.Age.CompareTo(b.Age): Returns a negative, zero, or positive number depending on how the two ages compare, exactly what Sort() needs to order the list.
  • people.Sort(new AgeComparer()): Hands the sorting logic off to the comparer instance, sorting the list in place by ascending age.

Exercise 29: Chunking a List

Practice Problem: Write a method that breaks a large List<int> into a list of smaller lists (chunks) of a specified size.

Purpose: This exercise helps you practice building a List<List<int>> from a flat list, stepping through the source in fixed-size strides with .GetRange() while carefully handling a final chunk that may be smaller than the rest.

Given Input: List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };, with a chunk size of 3.

Expected Output:

{1, 2, 3}
{4, 5, 6}
{7, 8, 9}
{10}
▼ Hint
  • Loop over the source list with a for loop, stepping forward by chunkSize each iteration instead of by 1.
  • At each step, use Math.Min(chunkSize, source.Count - i) to figure out how many elements are actually left to take.
  • Call source.GetRange(i, remaining) to grab that chunk, and add it to the result list of chunks.
  • This naturally handles a final chunk that has fewer elements than chunkSize.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class ListChunker
{
    public static List<List<int>> ChunkList(List<int> source, int chunkSize)
    {
        List<List<int>> chunks = new List<List<int>>();

        for (int i = 0; i < source.Count; i += chunkSize)
        {
            int remaining = Math.Min(chunkSize, source.Count - i);
            chunks.Add(source.GetRange(i, remaining));
        }

        return chunks;
    }
}

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        List<List<int>> chunks = ListChunker.ChunkList(numbers, 3);

        foreach (List<int> chunk in chunks)
        {
            Console.WriteLine("{" + string.Join(", ", chunk) + "}");
        }
    }
}Code language: C# (cs)

Explanation:

  • for (int i = 0; i < source.Count; i += chunkSize): Jumps forward by a full chunk’s worth of elements on every iteration, instead of one at a time.
  • Math.Min(chunkSize, source.Count - i): Prevents the final chunk from trying to read past the end of the source list when it has fewer than chunkSize elements left.
  • source.GetRange(i, remaining): Extracts exactly that chunk’s worth of elements as its own independent list.

Exercise 30: Flattening Lists

Practice Problem: Create a List<List<int>>, a list of integer lists. Use LINQ’s .SelectMany() to flatten it into a single List<int>.

Purpose: This exercise helps you practice .SelectMany(), which is specifically designed to collapse a sequence of sequences into one flat sequence, unlike Select() which would leave the nested lists intact.

Given Input: Three inner lists, { 1, 2, 3 }, { 4, 5 }, and { 6, 7, 8, 9 }.

Expected Output: {1, 2, 3, 4, 5, 6, 7, 8, 9}

▼ Hint
  • Call nested.SelectMany(list => list) directly on the outer list.
  • Each inner list gets expanded and merged into one continuous sequence, in the order the inner lists themselves appear.
  • Chain .ToList() at the end to materialize the flattened sequence into an actual List<int>.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<List<int>> nested = new List<List<int>>
        {
            new List<int> { 1, 2, 3 },
            new List<int> { 4, 5 },
            new List<int> { 6, 7, 8, 9 }
        };

        List<int> flattened = nested.SelectMany(list => list).ToList();

        Console.WriteLine("{" + string.Join(", ", flattened) + "}");
    }
}Code language: C# (cs)

Explanation:

  • List<List<int>> nested: Represents a list where each element is itself a full list of integers, rather than a single integer.
  • nested.SelectMany(list => list): Projects each inner list to itself and merges all of them into one single, flat sequence.
  • .ToList(): Converts the flattened sequence into a concrete List<int> that can be stored, indexed, or printed like any other list.

Exercise 31: Binary Search

Practice Problem: Create a list of 1,000 sorted integers. Use the .BinarySearch() method to efficiently find the index of a specific number.

Purpose: This exercise helps you practice .BinarySearch(), which locates a value in a sorted list far faster than scanning from the start, but only works correctly if the list is actually sorted beforehand.

Given Input: A list of 1,000 even numbers, 0, 2, 4, and so on up to 1998, searching for the value 742.

Expected Output: Index of 742 = 371

▼ Hint
  • Build the list so its values are already in ascending order, since .BinarySearch() assumes this and gives unreliable results otherwise.
  • Call numbers.BinarySearch(742) directly on the sorted list.
  • The returned index tells you exactly where that value sits, found in far fewer comparisons than a simple linear scan would need.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            numbers.Add(i * 2);
        }

        int index = numbers.BinarySearch(742);

        Console.WriteLine("Index of 742 = " + index);
    }
}Code language: C# (cs)

Explanation:

  • numbers.Add(i * 2): Builds a list of even numbers, 0, 2, 4, all the way up to 1998, already in sorted order.
  • numbers.BinarySearch(742): Repeatedly checks the middle of the remaining range and narrows the search by half each time, instead of checking every element in order.
  • Result, 371: Since every value in the list is exactly double its index, searching for 742 correctly lands on index 371.

Exercise 32: Dictionary Conversion

Practice Problem: Given a List<Student>, convert it into a Dictionary<int, Student> where the key is the Student.Id, using LINQ’s .ToDictionary().

Purpose: This exercise helps you practice turning a flat list of objects into a dictionary keyed by one of that object’s own properties, making later lookups by Id instant instead of requiring a scan through the whole list.

Given Input: Three students with Ids 1, 2, and 3, named Alice, Bob, and Charlie.

Expected Output: Student with Id 2: Bob

▼ Hint
  • Call students.ToDictionary(s => s.Id), using just one lambda for the key selector.
  • When only a key selector is given, ToDictionary() uses the whole original object as the value automatically.
  • Look up a specific student directly with the dictionary’s indexer, like studentById[2].
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Id = 1, Name = "Alice" },
            new Student { Id = 2, Name = "Bob" },
            new Student { Id = 3, Name = "Charlie" }
        };

        Dictionary<int, Student> studentById = students.ToDictionary(s => s.Id);

        Console.WriteLine("Student with Id 2: " + studentById[2].Name);
    }
}Code language: C# (cs)

Explanation:

  • students.ToDictionary(s => s.Id): Builds a dictionary where each student’s Id becomes the key and the full Student object becomes the value.
  • studentById[2]: Retrieves the exact student whose Id is 2 in a single, constant-time lookup.
  • .Name: Reads a property directly off the retrieved Student object, since the dictionary’s value is the whole object, not just a name string.

Exercise 33: Check Conditions (All/Any)

Practice Problem: Given a list of exam scores, use LINQ to check if any student scored 100, and if all students passed, scored above 50.

Purpose: This exercise helps you practice the difference between .Any(), which asks whether at least one element satisfies a condition, and .All(), which asks whether every element satisfies it.

Given Input: List<int> scores = new List<int> { 85, 100, 62, 78, 45, 90 };

Expected Output:

Any student scored 100? True
Did all students pass? False
▼ Hint
  • Call scores.Any(score => score == 100), which stops and returns true as soon as one match is found.
  • Call scores.All(score => score > 50), which stops and returns false as soon as one non-matching score is found.
  • A single low score is enough to make All() return false, even if every other score easily passes.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<int> scores = new List<int> { 85, 100, 62, 78, 45, 90 };

        bool anyPerfectScore = scores.Any(score => score == 100);
        bool allPassed = scores.All(score => score > 50);

        Console.WriteLine("Any student scored 100? " + anyPerfectScore);
        Console.WriteLine("Did all students pass? " + allPassed);
    }
}Code language: C# (cs)

Explanation:

  • scores.Any(score => score == 100): Returns true because at least one score in the list is exactly 100.
  • scores.All(score => score > 50): Returns false because the score of 45 fails the condition, even though every other score passes.
  • Short-circuiting: Both methods stop scanning as soon as the overall answer is already decided, rather than always checking every element.

Exercise 34: Rotate Elements

Practice Problem: Write a function to rotate a List<T> to the left by k positions, for example, rotating {1, 2, 3, 4} by 1 position results in {2, 3, 4, 1}.

Purpose: This exercise helps you practice building a generic method that works for a list of any element type, and shows how .GetRange() can split a list into two pieces that are then reassembled in swapped order.

Given Input: List<int> numbers = new List<int> { 1, 2, 3, 4 };, rotating left by 1.

Expected Output: {2, 3, 4, 1}

▼ Hint
  • Take the modulo of k by the list’s length first, so a k larger than the list still works correctly.
  • Grab the elements from index k to the end with .GetRange(k, count - k).
  • Grab the first k elements with .GetRange(0, k).
  • Append the second piece onto the end of the first piece to get the rotated result.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

public static class ListRotator
{
    public static List<T> RotateLeft<T>(List<T> source, int k)
    {
        int count = source.Count;
        if (count == 0)
            return new List<T>(source);

        k = k % count;

        List<T> rotated = new List<T>(source.GetRange(k, count - k));
        rotated.AddRange(source.GetRange(0, k));

        return rotated;
    }
}

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4 };

        List<int> rotated = ListRotator.RotateLeft(numbers, 1);

        Console.WriteLine("{" + string.Join(", ", rotated) + "}");
    }
}Code language: C# (cs)

Explanation:

  • k = k % count: Wraps k around the list’s actual length, so rotating by an amount larger than the list still produces a correct result.
  • source.GetRange(k, count - k): Takes everything from index k onward, which becomes the front of the rotated list.
  • source.GetRange(0, k): Takes the first k elements, which get moved to the back of the rotated list.
  • RotateLeft<T>: Works for a list of any element type, not just integers, since the method itself is generic.

Exercise 35: Frequency Count

Practice Problem: Given a list of words, use LINQ to count the frequency of each word and output the results as a list of anonymous objects showing the word and its count.

Purpose: This exercise helps you practice combining GroupBy() with an anonymous type projection, a handy way to shape query results into a lightweight, one-off structure without declaring a dedicated class first.

Given Input: { "apple", "banana", "apple", "cherry", "apple", "banana" }

Expected Output:

apple: 3
banana: 2
cherry: 1
▼ Hint
  • Call GroupBy(word => word) to cluster identical words together.
  • Chain Select(group => new { Word = group.Key, Count = group.Count() }) to shape each group into a small anonymous object.
  • Since an anonymous type has no name you can write out, the variable holding the results must be declared with var instead of an explicit type.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<string> words = new List<string> { "apple", "banana", "apple", "cherry", "apple", "banana" };

        var frequencies = words
            .GroupBy(word => word)
            .Select(group => new { Word = group.Key, Count = group.Count() })
            .ToList();

        foreach (var entry in frequencies)
        {
            Console.WriteLine(entry.Word + ": " + entry.Count);
        }
    }
}Code language: C# (cs)

Explanation:

  • GroupBy(word => word): Clusters every occurrence of the same word into its own group.
  • new { Word = group.Key, Count = group.Count() }: Builds a small anonymous object per group, carrying just the word itself and how many times it appeared.
  • var frequencies and var entry: Required here since anonymous types don’t have a name that can be written out explicitly, this is one of the few places var is necessary rather than optional.

Filed Under: C# Exercises

Did you find this page helpful? Let others know about it. Sharing helps me continue to create free Python resources.

TweetF  sharein  shareP  Pin

About Vishal

I’m Vishal Hule, the Founder of PYnative.com. As a Python developer, I enjoy assisting students, developers, and learners. Follow me on Twitter.

Related Tutorial Topics:

C# Exercises

All Coding Exercises:

C Exercises
C++ Exercises
Python Exercises
Java Exercises
C# Exercises

Python Exercises and Quizzes

Free coding exercises and quizzes cover Python basics, data structure, data analytics, and more.

  • 15+ Topic-specific Exercises and Quizzes
  • Each Exercise contains 25+ questions
  • Each Quiz contains 25 MCQ
Exercises
Quizzes

Leave a Reply Cancel reply

your email address will NOT be published. all comments are moderated according to our comment policy.

Use <pre> tag for posting code. E.g. <pre> Your entire code </pre>

In: C# Exercises
TweetF  sharein  shareP  Pin

  C# Exercises

  • C# Exercise for Beginners
  • C# Loops Exercise
  • C# Array Exercise
  • C# String Exercise
  • C# OOP Exercise
  • C# Structs, Records and Enums Exercise
  • C# Collections Exercise
  • C# List Exercise
  • C# Dictionary Exercise
  • C# LINQ Exercise
  • C# Exception Handling Exercise
  • C# File Handling Exercise
  • C# Date and Time Exercise
  • C# Generics Exercise
  • C# Lambda Expressions Exercise
  • C# Delegates and Events Exercise
  • C# Extension Methods Exercise
  • C# Regex Exercise
  • C# Pattern Matching Exercise
  • C# Iterators and Yield Exercise
  • C# Indexers and Operator Overloading Exercise
  • C# Random Data Generation Exercise

All Coding Exercises

Python Exercises C Exercises C++ Exercises Java Exercises C# Exercises

About PYnative

PYnative.com is for Python lovers. Here, You can get Tutorials, Exercises, and Quizzes to practice and improve your Python skills.

Follow Us

To get New Python Tutorials, Exercises, and Quizzes

  • Twitter
  • Facebook
  • Sitemap

Explore Python

  • Learn Python
  • Python Basics
  • Python Databases
  • Python Exercises
  • Python Quizzes
  • Online Python Code Editor
  • Python Tricks

Coding Exercises

  • C Exercises
  • C++ Exercises
  • Python Exercises
  • Java Exercises
  • C# Exercises

Legal Stuff

  • About Us
  • Contact Us

We use cookies to improve your experience. While using PYnative, you agree to have read and accepted our:

  • Terms Of Use
  • Privacy Policy
  • Cookie Policy

Copyright © 2018–2026 pynative.com