1. Introducción
Trabajar con colecciones no es solo recorrer, filtrar y ordenar. Muy a menudo tienes que cambiar o transformar esas colecciones para que se adapten a lo que necesitas: por ejemplo, coger una colección de strings y convertirla en una colección de números, eliminar duplicados de una lista o cambiar completamente la estructura de los datos.
Todas estas tareas se pueden resolver usando los métodos estándar de las colecciones, bucles simples y algoritmos básicos. Es un poco más largo que usando LINQ, pero es totalmente transparente y útil para entender cómo funcionan las colecciones.
Formas principales de modificar colecciones
Puedes modificar colecciones de varias maneras, y normalmente necesitas uno de estos enfoques:
- Modificar el contenido: añadir, borrar o reemplazar elementos.
- Transformar elementos: coger la colección original y obtener una nueva basada en ella — de otra estructura, tipo o forma (por ejemplo, de List<int> a List<string>).
- Cambiar la presentación: reestructurar la colección — por ejemplo, ordenarla, invertirla (reverse) o agruparla.
Nos vamos a centrar en el primer y segundo grupo, porque la ordenación ya la vimos en otra lección.
Colecciones mutables e inmutables: un matiz importante
Recuerda que algunas colecciones en .NET se pueden modificar libremente (por ejemplo, List<T>), y otras no (IReadOnlyList<T>, arrays con modificadores de solo lectura, etc.).
- Modificación — cambiamos la colección concreta (por ejemplo, usando Add, Remove, Clear, etc.).
- Transformación — no tocamos el original, sino que obtenemos una colección nueva, normalmente creando una lista nueva y llenándola con los elementos de la colección original.
2. Modificar el contenido de las colecciones
Todas las colecciones que implementan ICollection<T> ofrecen métodos básicos para cambiar el contenido. Vamos a verlos con el ejemplo de un catálogo de libros.
Añadir y borrar elementos
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 = "Código Limpio", Author = "Robert Martin", Year = 2008 },
new Book { Title = "CLR via C#", Author = "Jeffrey Richter", Year = 2012 }
};
// Añadir un libro nuevo
books.Add(new Book { Title = "Head First C#", Author = "Andrew Stellman", Year = 2010 });
// Borrar un libro por condición (por ejemplo, por autor)
for (int i = books.Count - 1; i >= 0; i--)
{
if (books[i].Author == "Robert Martin")
books.RemoveAt(i);
}
// Borrar un objeto concreto (si está en la lista)
Book someBook = books[0];
books.Remove(someBook);
// Vaciar toda la colección
books.Clear();
// Insertar un elemento en una posición concreta
books.Insert(0, new Book { Title = "Pro C# 9", Author = "Andrew Troelsen", Year = 2021 });
Reemplazar y actualizar elementos
Por ejemplo, alguien se equivocó con el año de publicación. ¿Cómo lo arreglamos?
// Buscamos el libro que queremos y corregimos el año
for (int i = 0; i < books.Count; i++)
{
if (books[i].Title == "Head First C#")
{
books[i].Year = 2018; // Corregimos el año
break;
}
}
3. Transformar colecciones
Transformar el tipo de los elementos (por ejemplo, de Book a string)
Obtener la colección de títulos de todos los libros:
List<string> bookTitles = new List<string>();
foreach (Book book in books)
{
bookTitles.Add(book.Title);
}
foreach (var title in bookTitles)
{
Console.WriteLine(title);
}
Obtener los años de todos los libros:
List<int> bookYears = new List<int>();
foreach (Book book in books)
{
bookYears.Add(book.Year);
}
Transformar a un nuevo tipo/estructura:
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})");
}
Transformar una colección de colecciones en una sola (flat map)
Si un libro tiene una lista de etiquetas, obtener una lista única de todas las etiquetas:
public class Book
{
public string Title { get; set; }
public List<string> Tags { get; set; }
}
List<Book> booksWithTags = new List<Book>
{
new Book { Title = "Código Limpio", 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. Métodos de transformación de colecciones más usados
ToArray
Para obtener un array a partir de una lista:
string[] bookTitlesArray = bookTitles.ToArray();
Distinct — eliminar duplicados
Autores únicos (sin LINQ):
List<string> authors = new List<string>();
foreach (Book book in books)
{
if (!authors.Contains(book.Author))
authors.Add(book.Author);
}
Reverse — invertir la colección
Para cambiar el orden al inverso:
books.Reverse(); // ¡modifica la colección original!
Si no quieres cambiar el original:
var reversed = new List<Book>(books);
reversed.Reverse();
Ordenar (ver la lección sobre ordenación)
books.Sort((a, b) => a.Year.CompareTo(b.Year)); // ordenar por año
Agrupar por campo (emulación de GroupBy)
Agrupar libros por autor (obtener un diccionario "autor — lista de libros"):
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. Matices útiles
- Las transformaciones usando bucles funcionan siempre y en cualquier sitio.
- Para eliminar duplicados, usa colecciones temporales y los métodos Contains/Add.
- ¡No cambies la colección mientras la recorres con foreach! Si tienes que borrar muchos elementos, primero recógelos en una lista aparte.
6. Ejemplos de tareas típicas
Obtener la lista de libros de un año concreto
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})");
}
Mostrar todos los años de publicación únicos en orden ascendente
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);
Preparar libros para exportar (crear una lista-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. Errores típicos y trampas
Los métodos como Add, Remove, Clear, Insert y Sort modifican la colección en el sitio.
Si necesitas una colección nueva — crea una lista nueva y añade ahí los elementos que quieras.
Al borrar elementos mientras recorres la colección, usa un bucle desde el final (for (int i = Count-1; i >= 0; i--)).
Si necesitas una lista con elementos únicos, vigila los duplicados: usa Contains o HashSet<T>.
GO TO FULL VERSION