JavaRush /Курси /C# SELF /Зміна та перетворення колекцій

Зміна та перетворення колекцій

C# SELF
Рівень 29 , Лекція 3
Відкрита

1. Вступ

Робота з колекціями — це не лише перебирання, фільтрація та сортування. Дуже часто постає завдання змінити або перетворити ці колекції, щоб вони відповідали вашим потребам: наприклад, узяти колекцію рядків і перетворити її на колекцію чисел, видалити зі списку дублікати або повністю змінити структуру даних.

Усі ці завдання можна вирішити за допомогою стандартних методів колекцій, простих циклів і базових алгоритмів. Це трохи довше, ніж за допомогою LINQ, зате абсолютно прозоро й корисно для розуміння принципів роботи колекцій.

Основні способи зміни колекцій

Змінювати колекції можна по-різному, і найчастіше потрібен один із таких підходів:

  • Модифікація вмісту: додати, видалити або замінити елементи.
  • Перетворення елементів: взяти початкову колекцію і отримати на її основі нову — іншої структури, типу чи форми (наприклад, з List<int> зробити List<string>).
  • Зміна представлення: перебудувати колекцію — наприклад, відсортувати, розвернути (reverse) або згрупувати.

Ми зосередимося на першій і другій групі, адже сортування детально розглядали в окремій лекції.

Змінювані та незмінювані колекції: важливий нюанс

Варто пам’ятати, що деякі колекції у .NET можна змінювати (наприклад, List<T>), а інші — ні (IReadOnlyList<T>, масиви, оголошені лише для читання, тощо).

  • Модифікація — змінюємо конкретну колекцію (наприклад, через Add, Remove, Clear тощо).
  • Перетворення — не змінюємо оригінал, а отримуємо нову колекцію, зазвичай через створення нового списку й поелементне заповнення на основі початкової колекції.

2. Модифікація вмісту колекцій

Усі колекції, які реалізують ICollection<T>, надають базові методи для зміни вмісту. Розглянемо їх на прикладі каталогу книжок.

Додавання та видалення елементів

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 = "Чистий код", Author = "Роберт Мартін", Year = 2008 },
    new Book { Title = "CLR via C#", Author = "Джеффрі Ріхтер", Year = 2012 }
};

// Додавання нової книжки
books.Add(new Book { Title = "Head First C#", Author = "Ендрю Стеллман", Year = 2010 });

// Видалення книжки за умовою (наприклад, за автором)
for (int i = books.Count - 1; i >= 0; i--)
{
    if (books[i].Author == "Роберт Мартін")
        books.RemoveAt(i);
}

// Видалити конкретний об’єкт (якщо він є у списку)
Book someBook = books[0];
books.Remove(someBook);

// Очистити всю колекцію
books.Clear();

// Вставити елемент у певну позицію
books.Insert(0, new Book { Title = "Pro C# 9", Author = "Ендрю Троелсен", Year = 2021 });

Заміна та оновлення елементів

Припустімо, хтось помилився з роком видання. Як це виправити?

// За допомогою циклу знайдемо потрібну книжку та виправимо рік
for (int i = 0; i < books.Count; i++)
{
    if (books[i].Title == "Head First C#")
    {
        books[i].Year = 2018; // Виправили рік
        break;
    }
}

3. Перетворення колекцій

Перетворення типу елементів (наприклад, з Book у рядок)

Отримати колекцію назв усіх книжок:

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

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

Отримати роки всіх книжок:

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

Перетворення на новий тип/структуру:

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

Перетворення колекції колекцій на одну (flat map)

Якщо в книжки є список тегів, отримайте єдиний список усіх тегів:

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

List<Book> booksWithTags = new List<Book>
{
    new Book { Title = "Чистий код", 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. Часто використовувані методи перетворення колекцій

ToArray

Щоб отримати масив зі списку:

string[] bookTitlesArray = bookTitles.ToArray();

Distinct — позбуваємося дублікатів

Унікальні автори (без LINQ):

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

Reverse — розвертаємо колекцію

Щоб змінити порядок на зворотний:

books.Reverse(); // змінює на місці!

Якщо не хочете змінювати оригінал:

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

Сортування (див. лекцію про сортування)

books.Sort((a, b) => a.Year.CompareTo(b.Year)); // сортування за роком

Групування за полем (емуляція GroupBy)

Згрупувати книжки за автором (отримати словник «автор — список книжок»):

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

5. Корисні нюанси

  • Операції перетворення за допомогою циклів працюють завжди й усюди.
  • Щоб прибрати дублікати, використовуйте тимчасові колекції та методи Contains/Add.
  • Не змінюйте колекцію під час її перебору через foreach! Якщо потрібно видалити багато елементів, спочатку зберіть їх в окремий список.

6. Приклади типових задач

Отримати список книжок після певного року

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

Вивести всі унікальні роки публікації за зростанням

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

Підготувати книжки до експорту (створити список-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. Типові помилки та підводні камені

Методи типу Add, Remove, Clear, Insert і Sort змінюють колекцію на місці.

Якщо вам потрібна нова колекція — створюйте новий список і додавайте туди потрібні елементи.

Під час видалення елементів під час обходу колекції використовуйте перебір з кінця (for (int i = Count-1; i >= 0; i--)).

Якщо потрібен список з унікальними елементами, стежте за повтореннями: використовуйте Contains або HashSet<T>.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ