CodeGym /Kursy /C# SELF /Modyfikacja i przekształcanie kolekcji

Modyfikacja i przekształcanie kolekcji

C# SELF
Poziom 29 , Lekcja 3
Dostępny

1. Wprowadzenie

Praca z kolekcjami to nie tylko iteracja, filtrowanie i sortowanie. Bardzo często pojawia się zadanie, żeby zmienić albo przerobić te kolekcje, żeby pasowały do twoich potrzeb: na przykład wziąć kolekcję stringów i zamienić ją na kolekcję liczb, usunąć duplikaty z listy albo całkowicie zmienić strukturę danych.

Wszystkie te zadania można ogarnąć za pomocą standardowych metod kolekcji, prostych pętli i bazowych algorytmów. To trochę dłużej niż z LINQ, ale za to totalnie przejrzyste i mega przydatne do zrozumienia jak działają kolekcje.

Podstawowe sposoby zmiany kolekcji

Kolekcje można zmieniać na różne sposoby, ale najczęściej chodzi o jeden z tych podejść:

  • Modyfikacja zawartości: dodanie, usunięcie albo podmiana elementów.
  • Przekształcenie elementów: wziąć oryginalną kolekcję i na jej podstawie zrobić nową — innej struktury, typu albo formy (np. z List<int> zrobić List<string>).
  • Zmiana widoku: przebudować kolekcję — np. posortować, odwrócić (reverse) albo pogrupować.

Skupimy się na pierwszej i drugiej grupie, bo sortowanie było już dokładnie omówione na osobnym wykładzie.

Kolekcje mutowalne i niemutowalne: ważny niuans

Warto pamiętać, że niektóre kolekcje w .NET można swobodnie zmieniać (np. List<T>), a inne — nie (IReadOnlyList<T>, tablice tylko do odczytu itd.).

  • Modyfikacja — zmieniamy konkretną kolekcję (np. przez Add, Remove, Clear itd.).
  • Przekształcenie — nie ruszamy oryginału, tylko robimy nową kolekcję, zwykle przez stworzenie nowej listy i wypełnienie jej elementami z oryginalnej kolekcji.

2. Modyfikacja zawartości kolekcji

Wszystkie kolekcje implementujące ICollection<T> mają bazowe metody do zmiany zawartości. Zobaczmy to na przykładzie katalogu książek.

Dodawanie i usuwanie elementów

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 = "Czysty kod", Author = "Robert Martin", Year = 2008 },
    new Book { Title = "CLR via C#", Author = "Jeffrey Richter", Year = 2012 }
};

// Dodanie nowej książki
books.Add(new Book { Title = "Head First C#", Author = "Andrew Stellman", Year = 2010 });

// Usunięcie książki po warunku (np. po autorze)
for (int i = books.Count - 1; i >= 0; i--)
{
    if (books[i].Author == "Robert Martin")
        books.RemoveAt(i);
}

// Usunąć konkretny obiekt (jeśli jest na liście)
Book someBook = books[0];
books.Remove(someBook);

// Wyczyścić całą kolekcję
books.Clear();

// Wstawić element na określoną pozycję
books.Insert(0, new Book { Title = "Pro C# 9", Author = "Andrew Troelsen", Year = 2021 });

Podmiana i aktualizacja elementów

Załóżmy, że ktoś się pomylił z rokiem wydania. Jak to poprawić?

// Znajdź odpowiednią książkę przez pętlę i popraw rok
for (int i = 0; i < books.Count; i++)
{
    if (books[i].Title == "Head First C#")
    {
        books[i].Year = 2018; // Poprawiony rok
        break;
    }
}

3. Przekształcanie kolekcji

Zmiana typu elementów (np. z Book na string)

Uzyskaj kolekcję tytułów wszystkich książek:

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

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

Uzyskaj lata wszystkich książek:

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

Przekształcenie do nowego typu/struktury:

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})");
}

Przekształcenie kolekcji kolekcji w jedną (flat map)

Jeśli książka ma listę tagów, uzyskaj jedną listę wszystkich tagów:

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

List<Book> booksWithTags = new List<Book>
{
    new Book { Title = "Czysty kod", 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. Często używane metody przekształcania kolekcji

ToArray

Żeby uzyskać tablicę z listy:

string[] bookTitlesArray = bookTitles.ToArray();

Distinct — pozbywamy się duplikatów

Unikalni autorzy (bez LINQ):

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

Reverse — odwracamy kolekcję

Żeby zmienić kolejność na odwrotną:

books.Reverse(); // zmienia na miejscu!

Jeśli nie chcesz zmieniać oryginału:

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

Sortowanie (patrz wykład o sortowaniu)

books.Sort((a, b) => a.Year.CompareTo(b.Year)); // sortowanie po roku

Grupowanie po polu (emulacja GroupBy)

Pogrupuj książki po autorze (uzyskaj słownik "autor — lista książek"):

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($"Autor: {pair.Key}");
    foreach (var b in pair.Value)
        Console.WriteLine($"  {b.Title}");
}

5. Przydatne niuanse

  • Operacje przekształcania przez pętle działają zawsze i wszędzie.
  • Żeby wykluczyć duplikaty, używaj tymczasowych kolekcji i metod Contains/Add.
  • Nie zmieniaj kolekcji podczas jej iterowania przez foreach! Jeśli musisz usunąć dużo elementów, najpierw zbierz je do osobnej listy.

6. Przykłady typowych zadań

Uzyskaj listę książek z określonego roku

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})");
}

Wyświetl wszystkie unikalne lata publikacji rosnąco

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);

Przygotuj książki do eksportu (stwórz listę-DTO)

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. Typowe błędy i pułapki

Metody typu Add, Remove, Clear, Insert i Sort zmieniają kolekcję na miejscu.

Jeśli potrzebujesz nowej kolekcji — stwórz nową listę i dodaj tam potrzebne elementy.

Przy usuwaniu elementów podczas iteracji po kolekcji używaj pętli od końca (for (int i = Count-1; i >= 0; i--)).

Jeśli potrzebujesz listy z unikalnymi elementami, pilnuj powtórzeń: używaj Contains albo HashSet<T>.

2
Zadanie
C# SELF, poziom 29, lekcja 3
Niedostępne
Dodawanie, usuwanie i modyfikacja elementów w kolekcji
Dodawanie, usuwanie i modyfikacja elementów w kolekcji
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION