CodeGym /Courses /C# SELF /Changing and Transforming Collections

Changing and Transforming Collections

C# SELF
Level 29 , Lesson 3
Available

1. Introduction

Working with collections isn't just about looping, filtering, and sorting. Very often, you need to change or rework these collections so they fit your needs: for example, take a collection of strings and turn it into a collection of numbers, remove duplicates from a list, or completely change the data structure.

You can solve all these tasks using standard collection methods, simple loops, and basic algorithms. It's a bit longer than using LINQ, but it's totally transparent and super useful for understanding how collections work under the hood.

Main ways to change collections

There are a bunch of ways to change collections, but usually you need one of these approaches:

  • Modifying the contents: add, remove, or replace elements.
  • Transforming elements: take the original collection and get a new one out of it — with a different structure, type, or shape (like turning a List<int> into a List<string>).
  • Changing the view: rebuild the collection — for example, sort it, reverse it, or group it.

We're gonna focus on the first two groups, since sorting was covered in detail in a separate lecture.

Mutable and immutable collections: a key thing to know

Keep in mind that some collections in .NET can be freely changed (like List<T>), and others can't (IReadOnlyList<T>, arrays with readonly modifiers, etc.).

  • Modification — we change the actual collection (for example, using Add, Remove, Clear, etc.).
  • Transformation — we leave the original alone and get a new collection, usually by creating a new list and filling it with elements from the original collection.

2. Modifying collection contents

All collections that implement ICollection<T> give you basic methods for changing their contents. Let's check them out using a book catalog as an example.

Adding and removing elements

public class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
    public int Year { get; set; }
}

List<Book> books = new List<Book>
{
    new Book { Title = "Clean Code", Author = "Robert Martin", Year = 2008 },
    new Book { Title = "CLR via C#", Author = "Jeffrey Richter", Year = 2012 }
};

// Adding a new book
books.Add(new Book { Title = "Head First C#", Author = "Andrew Stellman", Year = 2010 });

// Removing a book by condition (for example, by author)
for (int i = books.Count - 1; i >= 0; i--)
{
    if (books[i].Author == "Robert Martin")
        books.RemoveAt(i);
}

// Remove a specific object (if it's in the list)
Book someBook = books[0];
books.Remove(someBook);

// Clear the whole collection
books.Clear();

// Insert an element at a specific position
books.Insert(0, new Book { Title = "Pro C# 9", Author = "Andrew Troelsen", Year = 2021 });

Replacing and updating elements

Let's say someone messed up the publication year. How do you fix it?

// Find the right book in a loop and fix the year
for (int i = 0; i < books.Count; i++)
{
    if (books[i].Title == "Head First C#")
    {
        books[i].Year = 2018; // Fixed the year
        break;
    }
}

3. Transforming collections

Transforming element types (like from Book to string)

Get a collection of all book titles:

List<string> bookTitles = new List<string>();
foreach (Book book in books)
{
    bookTitles.Add(book.Title);
}

foreach (var title in bookTitles)
{
    Console.WriteLine(title);
}

Get all the years from the books:

List<int> bookYears = new List<int>();
foreach (Book book in books)
{
    bookYears.Add(book.Year);
}

Transform into a new type/structure:

public class BriefBookInfo
{
    public string Title;
    public int Year;
}

List<BriefBookInfo> briefInfos = new List<BriefBookInfo>();
foreach (Book book in books)
{
    briefInfos.Add(new BriefBookInfo { Title = book.Title, Year = book.Year });
}

foreach (var info in briefInfos)
{
    Console.WriteLine($"{info.Title} ({info.Year})");
}

Flattening a collection of collections (flat map)

If a book has a list of tags, get a single list of all tags:

public class Book
{
    public string Title { get; set; }
    public List<string> Tags { get; set; }
}

List<Book> booksWithTags = new List<Book>
{
    new Book { Title = "Clean Code", Tags = new List<string> { "Clean Code", "Refactoring" } },
    new Book { Title = "CLR via C#", Tags = new List<string> { "CLR", "Internals" } }
};

List<string> allTags = new List<string>();
foreach (var book in booksWithTags)
{
    foreach (var tag in book.Tags)
    {
        allTags.Add(tag);
    }
}

foreach (string tag in allTags)
{
    Console.WriteLine(tag);
}

4. Commonly used collection transformation methods

ToArray

To get an array from a list:

string[] bookTitlesArray = bookTitles.ToArray();

Distinct — getting rid of duplicates

Unique authors (without LINQ):

List<string> authors = new List<string>();
foreach (Book book in books)
{
    if (!authors.Contains(book.Author))
        authors.Add(book.Author);
}

Reverse — flipping a collection

To reverse the order:

books.Reverse(); // changes in place!

If you don't want to mess with the original:

var reversed = new List<Book>(books);
reversed.Reverse();

Sorting (see the sorting lecture)

books.Sort((a, b) => a.Year.CompareTo(b.Year)); // sort by year

Grouping by field (GroupBy emulation)

Group books by author (get a dictionary "author — list of books"):

Dictionary<string, List<Book>> booksByAuthor = new Dictionary<string, List<Book>>();
foreach (Book book in books)
{
    if (!booksByAuthor.ContainsKey(book.Author))
        booksByAuthor[book.Author] = new List<Book>();
    booksByAuthor[book.Author].Add(book);
}

foreach (var pair in booksByAuthor)
{
    Console.WriteLine($"Author: {pair.Key}");
    foreach (var b in pair.Value)
        Console.WriteLine($"  {b.Title}");
}

5. Handy tips

  • Transformation operations using loops always work, everywhere.
  • To get rid of duplicates, use temp collections and the Contains/Add methods.
  • Don't change a collection while looping through it with foreach! If you need to remove a bunch of elements, collect them in a separate list first.

6. Typical tasks examples

Get a list of books from a certain year

List<Book> recentBooks = new List<Book>();
foreach (Book book in books)
{
    if (book.Year > 2010)
        recentBooks.Add(book);
}
foreach (var book in recentBooks)
{
    Console.WriteLine($"{book.Title} ({book.Year})");
}

Print all unique publication years in ascending order

List<int> years = new List<int>();
foreach (Book book in books)
{
    if (!years.Contains(book.Year))
        years.Add(book.Year);
}
years.Sort();

foreach (int year in years)
    Console.WriteLine(year);

Prepare books for export (create a DTO list)

public class BookExport
{
    public string Name;
    public string Writer;
    public int PublishedYear;
}

List<BookExport> booksForExport = new List<BookExport>();
foreach (Book book in books)
{
    booksForExport.Add(new BookExport
    {
        Name = book.Title,
        Writer = book.Author,
        PublishedYear = book.Year
    });
}

7. Typical mistakes and gotchas

Methods like Add, Remove, Clear, Insert and Sort change the collection in place.

If you need a new collection — make a new list and add the elements you want there.

When removing elements while looping through a collection, use a backwards loop (for (int i = Count-1; i >= 0; i--)).

If you need a list with unique elements, watch out for repeats: use Contains or HashSet<T>.

2
Task
C# SELF, level 29, lesson 3
Locked
Adding, Removing, and Modifying Elements in a Collection
Adding, Removing, and Modifying Elements in a Collection
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION