Capítulo 9: Colecciones de datos
Capítulo 9: Colecciones de datos
Sección titulada «Capítulo 9: Colecciones de datos»Parte I - Sección 2: Programación desde cero - Nivel: Principiante
🎯 Objetivos de aprendizaje
Sección titulada «🎯 Objetivos de aprendizaje»Al finalizar este capítulo serás capaz de:
- Entender qué es una colección y por qué necesitamos más que variables simples
- Trabajar con arrays de tamaño fijo
- Usar List
para colecciones dinámicas que crecen y decrecen - Manejar Dictionary<K,V> para pares clave-valor
- Elegir la colección adecuada según el problema
- Recorrer colecciones con foreach, for, y acceso directo
- Construir un gestor completo de lista de materiales con búsqueda y filtrado
📋 Requisitos previos
Sección titulada «📋 Requisitos previos»Antes de comenzar este capítulo debes:
- ✅ Haber completado el Capítulo 8
- ✅ Entender funciones y métodos
- ✅ Conocer bucles (for, foreach, while)
- ✅ Saber trabajar con variables y tipos de datos
🏆 Proyecto del capítulo
Sección titulada «🏆 Proyecto del capítulo»Gestor Inteligente de Lista de Materiales
Al final de este capítulo habrás construido un sistema completo para:
- Gestionar componentes eléctricos (agregar, eliminar, buscar)
- Buscar por código, tipo, o designación
- Calcular estadísticas (total de componentes, corriente total, etc.)
- Filtrar por criterios (corriente, voltaje, fabricante)
- Generar reportes formateados
Tiempo estimado: 60-70 minutos
⚠️ Nota Importante: En este capítulo, los datos de los componentes (designación, código, etc.) se crearán manualmente en el código. Esto se hace intencionalmente para centrarnos en el aprendizaje de Colecciones (Listas y Diccionarios) sin distraernos con la complejidad de leer datos reales de la base de datos de EPLAN, lo cual aprenderemos en capítulos posteriores.
📚 Contenido
Sección titulada «📚 Contenido»1. Introducción: El problema de las variables individuales
Sección titulada «1. Introducción: El problema de las variables individuales»Sin colecciones (código difícil de mantener):
// Necesitamos almacenar 100 componentes...string componente1 = "K1";string componente2 = "K2";string componente3 = "K3";// ... 97 variables másstring componente100 = "K100";
// ¿Cómo buscamos? ¿Cómo agregamos uno nuevo?// Imposible de mantenerCon colecciones (código escalable):
// Array simple de 100 elementosstring[] componentes = new string[100];componentes[0] = "K1";componentes[1] = "K2";// ...
// O mejor: List que crece dinámicamenteList<string> componentes = new List<string>();componentes.Add("K1");componentes.Add("K2");// Fácil agregar, eliminar, buscarLas colecciones nos permiten:
- Almacenar múltiples valores relacionados
- Agregar y eliminar elementos dinámicamente
- Buscar y filtrar datos eficientemente
- Iterar sobre todos los elementos fácilmente
- Organizar datos complejos
2. Arrays: Colecciones de tamaño fijo
Sección titulada «2. Arrays: Colecciones de tamaño fijo»Un array es una colección de elementos del mismo tipo con tamaño fijo.
2.1 Declaración y creación
Sección titulada «2.1 Declaración y creación»// Sintaxistipo[] nombreArray = new tipo[tamaño];
// Ejemplosstring[] componentes = new string[5];double[] corrientes = new double[10];int[] cantidades = new int[20];2.2 Inicialización
Sección titulada «2.2 Inicialización»Forma 1: Declarar y luego asignar
string[] componentes = new string[3];componentes[0] = "K1";componentes[1] = "Q1";componentes[2] = "F1";Forma 2: Inicializar directamente
string[] componentes = {"K1", "Q1", "F1"};// O más explícito:string[] componentes = new string[] {"K1", "Q1", "F1"};Forma 3: Con valores calculados
double[] voltajes = new double[5];for (int i = 0; i < voltajes.Length; i++){ voltajes[i] = 230.0 + i * 10.0; // 230, 240, 250, 260, 270}2.3 Acceso a elementos
Sección titulada «2.3 Acceso a elementos»string[] componentes = {"K1", "Q1", "F1", "M1", "X1"};
// Acceso por índice (comienza en 0)string primero = componentes[0]; // "K1"string ultimo = componentes[4]; // "X1"
// Modificar elementocomponentes[2] = "F2"; // Cambia "F1" por "F2"
// Longitud del arrayint total = componentes.Length; // 52.4 Recorrer arrays
Sección titulada «2.4 Recorrer arrays»Con for:
string[] componentes = {"K1", "Q1", "F1", "M1"};
for (int i = 0; i < componentes.Length; i++){ MessageBox.Show(string.Format("[{0}] = {1}", i, componentes[i]));}Con foreach (más simple):
foreach (string componente in componentes){ MessageBox.Show(componente);}2.5 Ejemplo práctico: Análisis de corrientes
Sección titulada «2.5 Ejemplo práctico: Análisis de corrientes»string[] designaciones = {"K1", "K2", "Q1", "Q2", "F1"};double[] corrientes = {25.0, 40.0, 16.0, 20.0, 10.0};
// Buscar la corriente máximadouble maxCorriente = corrientes[0];string designacionMax = designaciones[0];
for (int i = 1; i < corrientes.Length; i++){ if (corrientes[i] > maxCorriente) { maxCorriente = corrientes[i]; designacionMax = designaciones[i]; }}
MessageBox.Show(string.Format("Corriente máxima: {0}A en {1}", maxCorriente, designacionMax));// Corriente máxima: 40A en K22.6 Limitaciones de los arrays
Sección titulada «2.6 Limitaciones de los arrays»❌ Tamaño fijo: No se puede cambiar después de crear
string[] componentes = new string[5];// No puedes agregar un sexto elemento❌ No se pueden eliminar elementos: Solo sobrescribir
componentes[2] = null; // "Elimina" pero deja un hueco❌ No hay métodos convenientes como Add(), Remove(), Contains()
Solución: Usar List<T> para colecciones dinámicas.
3. List: Colecciones dinámicas
Sección titulada «3. List: Colecciones dinámicas»List<T> es una colección dinámica que puede crecer y decrecer automáticamente.
Ventajas sobre arrays:
- Tamaño dinámico (crece automáticamente)
- Métodos convenientes: Add(), Remove(), Insert(), Contains()
- Más flexible para la mayoría de casos
3.1 Declaración y creación
Sección titulada «3.1 Declaración y creación»// SintaxisList<tipo> nombreLista = new List<tipo>();
// EjemplosList<string> componentes = new List<string>();List<double> corrientes = new List<double>();List<int> cantidades = new List<int>();3.2 Agregar elementos
Sección titulada «3.2 Agregar elementos»List<string> componentes = new List<string>();
// Add: Agrega al finalcomponentes.Add("K1");componentes.Add("Q1");componentes.Add("F1");
// Insert: Agrega en posición específicacomponentes.Insert(1, "K2"); // Inserta "K2" en posición 1// Resultado: ["K1", "K2", "Q1", "F1"]3.3 Acceso a elementos
Sección titulada «3.3 Acceso a elementos»List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Acceso por índicestring primero = componentes[0]; // "K1"string segundo = componentes[1]; // "Q1"
// Count: número de elementosint total = componentes.Count; // 4
// Contains: verificar si existebool existe = componentes.Contains("K1"); // true3.4 Eliminar elementos
Sección titulada «3.4 Eliminar elementos»List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Remove: elimina la primera ocurrenciacomponentes.Remove("Q1");// Resultado: ["K1", "F1", "M1"]
// RemoveAt: elimina por índicecomponentes.RemoveAt(0);// Resultado: ["F1", "M1"]
// Clear: elimina todoscomponentes.Clear();// Resultado: []3.5 Buscar y filtrar
Sección titulada «3.5 Buscar y filtrar»List<string> componentes = new List<string> {"K1", "K2", "Q1", "Q2", "F1", "M1"};
// IndexOf: encontrar posiciónint indice = componentes.IndexOf("Q1"); // 2if (indice != -1){ MessageBox.Show("Encontrado en posición: " + indice);}
// Find: buscar con condiciónstring primerK = componentes.Find(c => c.StartsWith("K"));MessageBox.Show("Primer contactor: " + primerK); // "K1"
// FindAll: buscar todos que cumplan condiciónList<string> contactores = componentes.FindAll(c => c.StartsWith("K"));// Resultado: ["K1", "K2"]3.6 Ejemplo práctico: Gestión de componentes
Sección titulada «3.6 Ejemplo práctico: Gestión de componentes»List<string> componentes = new List<string>();
// Agregar componentescomponentes.Add("K1");componentes.Add("K2");componentes.Add("Q1");componentes.Add("F1");
// Mostrar todosstring lista = "Componentes:\n";foreach (string comp in componentes){ lista += "- " + comp + "\n";}MessageBox.Show(lista);
// Buscar si existe un componenteif (componentes.Contains("Q1")){ MessageBox.Show("Guardamotor Q1 encontrado");}
// Contar contactoresint numContactores = componentes.FindAll(c => c.StartsWith("K")).Count;MessageBox.Show("Número de contactores: " + numContactores); // 2
// Eliminar componentecomponentes.Remove("F1");MessageBox.Show("Componentes después de eliminar F1: " + componentes.Count); // 33.7 Recorrer List
Sección titulada «3.7 Recorrer List»Con foreach (recomendado):
List<double> corrientes = new List<double> {10.0, 16.0, 25.0, 32.0};
foreach (double corriente in corrientes){ MessageBox.Show(string.Format("Corriente: {0}A", corriente));}Con for (cuando necesitas el índice):
for (int i = 0; i < corrientes.Count; i++){ MessageBox.Show(string.Format("[{0}] = {1}A", i, corrientes[i]));}4. Dictionary<K,V>: Colecciones clave-valor
Sección titulada «4. Dictionary<K,V>: Colecciones clave-valor»Dictionary<K,V> almacena pares clave-valor para búsquedas rápidas.
Analogía: Un diccionario de idiomas
- Clave: Palabra en español
- Valor: Traducción en inglés
- Búsqueda rápida por clave
4.1 Declaración y creación
Sección titulada «4.1 Declaración y creación»// SintaxisDictionary<tipoClave, tipoValor> nombreDict = new Dictionary<tipoClave, tipoValor>();
// EjemplosDictionary<string, double> corrientes = new Dictionary<string, double>();Dictionary<string, string> fabricantes = new Dictionary<string, string>();Dictionary<int, string> componentes = new Dictionary<int, string>();4.2 Agregar elementos
Sección titulada «4.2 Agregar elementos»Dictionary<string, double> corrientes = new Dictionary<string, double>();
// Add: agregar par clave-valorcorrientes.Add("K1", 25.0);corrientes.Add("K2", 40.0);corrientes.Add("Q1", 16.0);
// O inicialización directaDictionary<string, double> corrientes = new Dictionary<string, double>{ {"K1", 25.0}, {"K2", 40.0}, {"Q1", 16.0}};4.3 Acceso a valores
Sección titulada «4.3 Acceso a valores»Dictionary<string, double> corrientes = new Dictionary<string, double>{ {"K1", 25.0}, {"K2", 40.0}, {"Q1", 16.0}};
// Acceso directo por clavedouble corrienteK1 = corrientes["K1"]; // 25.0
// Verificar si existe una claveif (corrientes.ContainsKey("K2")){ MessageBox.Show("K2 tiene corriente: " + corrientes["K2"]);}
// TryGetValue: forma segura (no lanza excepción)double valor;if (corrientes.TryGetValue("Q1", out valor)){ MessageBox.Show("Q1: " + valor + "A");}else{ MessageBox.Show("Q1 no encontrado");}4.4 Modificar y eliminar
Sección titulada «4.4 Modificar y eliminar»Dictionary<string, double> corrientes = new Dictionary<string, double>{ {"K1", 25.0}, {"K2", 40.0}};
// Modificar valor existentecorrientes["K1"] = 32.0;
// Eliminar entradacorrientes.Remove("K2");
// Count: número de entradasint total = corrientes.Count; // 14.5 Recorrer Dictionary
Sección titulada «4.5 Recorrer Dictionary»Con foreach (pares clave-valor):
Dictionary<string, double> corrientes = new Dictionary<string, double>{ {"K1", 25.0}, {"K2", 40.0}, {"Q1", 16.0}};
foreach (KeyValuePair<string, double> par in corrientes){ MessageBox.Show(string.Format("{0}: {1}A", par.Key, par.Value));}Forma simplificada con var:
foreach (var par in corrientes){ MessageBox.Show(string.Format("{0}: {1}A", par.Key, par.Value));}Solo claves o solo valores:
// Solo clavesforeach (string clave in corrientes.Keys){ MessageBox.Show(clave);}
// Solo valoresforeach (double valor in corrientes.Values){ MessageBox.Show(valor + "A");}4.6 Ejemplo práctico: Base de datos de componentes
Sección titulada «4.6 Ejemplo práctico: Base de datos de componentes»// Diccionario: Código → DescripciónDictionary<string, string> baseDatos = new Dictionary<string, string>{ {"3RT2026-1BB40", "Contactor 3P 25A AC3"}, {"3RT2036-1BB40", "Contactor 3P 40A AC3"}, {"3RV2011-1JA10", "Guardamotor 3.5-5A"}, {"LC1D09BD", "Contactor TeSys D 9A"}};
// Buscar componentestring codigoBuscar = "3RT2026-1BB40";
if (baseDatos.ContainsKey(codigoBuscar)){ string descripcion = baseDatos[codigoBuscar]; MessageBox.Show(string.Format("Código: {0}\nDescripción: {1}", codigoBuscar, descripcion));}else{ MessageBox.Show("Código no encontrado");}
// Listar todos los componentesstring reporte = "BASE DE DATOS DE COMPONENTES\n\n";foreach (var item in baseDatos){ reporte += string.Format("{0}\n → {1}\n\n", item.Key, item.Value);}MessageBox.Show(reporte);5. Comparación de colecciones
Sección titulada «5. Comparación de colecciones»| Característica | Array | List | Dictionary<K,V> |
|---|---|---|---|
| Tamaño | Fijo | Dinámico | Dinámico |
| Acceso | Por índice | Por índice | Por clave |
| Búsqueda | Lenta (O(n)) | Lenta (O(n)) | Rápida (O(1)) |
| Agregar | No | Sí (Add) | Sí (Add) |
| Eliminar | No | Sí (Remove) | Sí (Remove) |
| Uso típico | Datos fijos | Listas dinámicas | Búsquedas por clave |
Cuándo usar cada una:
Array string[]:
- Tamaño conocido y fijo
- No necesitas agregar/eliminar
- Ejemplo: Días de la semana, meses del año
ListList<string>:
- Tamaño variable
- Necesitas agregar/eliminar frecuentemente
- Ejemplo: Lista de componentes en un proyecto
Dictionary<K,V> Dictionary<string, double>:
- Búsquedas rápidas por clave única
- Asociar un valor con otro
- Ejemplo: Código de artículo → Descripción
💻 Manos a la obra: Construyendo el proyecto
Sección titulada «💻 Manos a la obra: Construyendo el proyecto»Vamos a crear un Gestor Inteligente de Lista de Materiales que combina todas las colecciones.
Estructura del proyecto
Sección titulada «Estructura del proyecto»using System;using System.Collections.Generic;using Eplan.EplApi.Base;using Eplan.EplApi.ApplicationFramework;
public class GestorListaMateriales{ // ======================================== // ESTRUCTURAS DE DATOS // ========================================
// Lista principal de componentes private List<Componente> componentes;
// Diccionario para búsqueda rápida por código private Dictionary<string, Componente> indiceCodigo;
// Estadísticas private Dictionary<string, int> contadorPorTipo;
[Start] public void Ejecutar() { // Código principal }}
// Clase auxiliar para representar un componentepublic class Componente{ public string Designacion { get; set; } public string Codigo { get; set; } public string Tipo { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; } public string Fabricante { get; set; }
public Componente(string designacion, string codigo, string tipo, double corriente, double voltaje, string fabricante) { this.Designacion = designacion; this.Codigo = codigo; this.Tipo = tipo; this.Corriente = corriente; this.Voltaje = voltaje; this.Fabricante = fabricante; }
public override string ToString() { return string.Format("{0} - {1} ({2}A, {3}V)", Designacion, Codigo, Corriente, Voltaje); }}Implementación completa
Sección titulada «Implementación completa»El código completo está disponible en code/cap-09/GestorListaMateriales.cs.
Funcionalidades incluidas:
- Agregar componentes con validación
- Buscar por designación, código, o tipo
- Eliminar componentes
- Filtrar por corriente, voltaje, fabricante
- Estadísticas: Total, corriente total, por tipo
- Reportes formateados
- Validaciones (duplicados, valores incorrectos)
🔍 Deep Dive: Entendiendo en profundidad
Sección titulada «🔍 Deep Dive: Entendiendo en profundidad»Complejidad de búsqueda
Sección titulada «Complejidad de búsqueda»Array y List
// Tiene que revisar todos los elementosList<string> componentes = new List<string> {"K1", "K2", "Q1", "F1"};bool existe = componentes.Contains("F1"); // O(n) - revisa 4 elementosDictionary<K,V>: Búsqueda por hash O(1)
// Acceso directo mediante hashDictionary<string, string> componentes = new Dictionary<string, string>{ {"K1", "Contactor"}, {"Q1", "Guardamotor"}};bool existe = componentes.ContainsKey("K1"); // O(1) - acceso directoConclusión: Para búsquedas frecuentes, usa Dictionary.
Colecciones anidadas
Sección titulada «Colecciones anidadas»Puedes combinar colecciones:
// Dictionary de ListsDictionary<string, List<string>> componentesPorTipo = new Dictionary<string, List<string>>{ {"Contactor", new List<string> {"K1", "K2", "K3"}}, {"Guardamotor", new List<string> {"Q1", "Q2"}}, {"Motor", new List<string> {"M1", "M2", "M3"}}};
// AccesoList<string> contactores = componentesPorTipo["Contactor"];foreach (string contactor in contactores){ MessageBox.Show(contactor);}LINQ: Consultas avanzadas
Sección titulada «LINQ: Consultas avanzadas»LINQ (Language Integrated Query) permite consultas poderosas:
List<Componente> componentes = ObtenerComponentes();
// Filtrar por corriente > 20Avar altaCorriente = componentes.Where(c => c.Corriente > 20.0).ToList();
// Ordenar por corrientevar ordenados = componentes.OrderBy(c => c.Corriente).ToList();
// Obtener solo designacionesvar designaciones = componentes.Select(c => c.Designacion).ToList();
// Suma total de corrientesdouble corrienteTotal = componentes.Sum(c => c.Corriente);
// Corriente promediodouble corrientePromedio = componentes.Average(c => c.Corriente);📝 Resumen
Sección titulada «📝 Resumen»En este capítulo aprendiste:
- ✅ Arrays: Colecciones de tamaño fijo, acceso rápido por índice
- ✅ List
: Colecciones dinámicas con Add/Remove/Insert - ✅ Dictionary<K,V>: Pares clave-valor para búsquedas rápidas
- ✅ Cuándo usar cada una: Según necesidades de tamaño y búsqueda
- ✅ Recorrer colecciones: foreach, for, acceso directo
- ✅ Proyecto construido: Gestor completo de lista de materiales
Conceptos clave:
- Las colecciones permiten manejar múltiples datos relacionados
- List
es la más versátil para la mayoría de casos - Dictionary<K,V> es ideal para búsquedas rápidas
- Siempre valida antes de acceder (ContainsKey, TryGetValue)
🔗 Conexiones
Sección titulada «🔗 Conexiones»⬅️ Capítulo anterior
Sección titulada «⬅️ Capítulo anterior»Capítulo 8: Funciones (Métodos)
➡️ Próximo capítulo
Sección titulada «➡️ Próximo capítulo»Capítulo 10: Clases y objetos (POO)
En el próximo capítulo aprenderás los fundamentos de Programación Orientada a Objetos (POO): clases, objetos, propiedades, constructores, y métodos. Construirás un sistema completo de gestión de circuitos eléctricos usando principios de POO.
📚 Recursos adicionales
Sección titulada «📚 Recursos adicionales»- 📖 Documentación de C# - Arrays
- 📖 Documentación de C# - List
- 📖 Documentación de C# - Dictionary<K,V>
- 💻 [Código completo]:
code/cap-09/
❓ Preguntas frecuentes
Sección titulada «❓ Preguntas frecuentes»P: ¿Cuándo debería usar un array en lugar de List?
Sección titulada «P: ¿Cuándo debería usar un array en lugar de List?»R: Usa arrays solo cuando el tamaño es fijo y conocido de antemano (ej. días de la semana: 7). En la mayoría de casos, List
P: ¿Puedo usar objetos complejos en colecciones?
Sección titulada «P: ¿Puedo usar objetos complejos en colecciones?»R: Sí. Puedes tener List<Componente>, Dictionary<string, Componente>, etc.
P: ¿Qué pasa si accedo a un índice fuera de rango?
Sección titulada «P: ¿Qué pasa si accedo a un índice fuera de rango?»R: Se lanza una excepción IndexOutOfRangeException. Siempre verifica con if (i < lista.Count).
P: ¿Puedo modificar una lista mientras la recorro con foreach?
Sección titulada «P: ¿Puedo modificar una lista mientras la recorro con foreach?»R: No. Esto causa una excepción. Si necesitas modificar, usa for en orden inverso o crea una lista temporal.
// Mal - causa excepciónforeach (var item in lista){ lista.Remove(item); // ❌ ERROR}
// Bien - usar for inversofor (int i = lista.Count - 1; i >= 0; i--){ lista.RemoveAt(i); // ✓ OK}P: ¿Dictionary permite claves duplicadas?
Sección titulada «P: ¿Dictionary permite claves duplicadas?»R: No. Cada clave debe ser única. Si intentas agregar una clave que ya existe, se lanza excepción. Usa ContainsKey() antes de agregar.
📋 Checklist de completitud
Sección titulada «📋 Checklist de completitud»Antes de pasar al siguiente capítulo, asegúrate de:
- Entender la diferencia entre Array, List, y Dictionary
- Poder crear y manipular List
(Add, Remove, Contains) - Poder usar Dictionary<K,V> para búsquedas rápidas
- Saber recorrer colecciones con foreach y for
- Comprender cuándo usar cada tipo de colección
- Haber ejecutado el gestor de lista de materiales
- Poder agregar nuevas funcionalidades al gestor
- (Opcional) Haber intentado al menos un desafío
¿Listo para aprender Programación Orientada a Objetos? Vamos al Capítulo 10! 🚀
Última actualización: Enero 2025
Tiempo de lectura estimado: 60-70 minutos
Código de ejemplo: code/cap-09/