Ir al contenido

Capítulo 9: Colecciones de datos

Parte I - Sección 2: Programación desde cero - Nivel: Principiante


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

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

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.


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ás
string componente100 = "K100";
// ¿Cómo buscamos? ¿Cómo agregamos uno nuevo?
// Imposible de mantener

Con colecciones (código escalable):

// Array simple de 100 elementos
string[] componentes = new string[100];
componentes[0] = "K1";
componentes[1] = "K2";
// ...
// O mejor: List que crece dinámicamente
List<string> componentes = new List<string>();
componentes.Add("K1");
componentes.Add("K2");
// Fácil agregar, eliminar, buscar

Las 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

Un array es una colección de elementos del mismo tipo con tamaño fijo.

// Sintaxis
tipo[] nombreArray = new tipo[tamaño];
// Ejemplos
string[] componentes = new string[5];
double[] corrientes = new double[10];
int[] cantidades = new int[20];

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
}
string[] componentes = {"K1", "Q1", "F1", "M1", "X1"};
// Acceso por índice (comienza en 0)
string primero = componentes[0]; // "K1"
string ultimo = componentes[4]; // "X1"
// Modificar elemento
componentes[2] = "F2"; // Cambia "F1" por "F2"
// Longitud del array
int total = componentes.Length; // 5

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áxima
double 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 K2

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.


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
// Sintaxis
List<tipo> nombreLista = new List<tipo>();
// Ejemplos
List<string> componentes = new List<string>();
List<double> corrientes = new List<double>();
List<int> cantidades = new List<int>();
List<string> componentes = new List<string>();
// Add: Agrega al final
componentes.Add("K1");
componentes.Add("Q1");
componentes.Add("F1");
// Insert: Agrega en posición específica
componentes.Insert(1, "K2"); // Inserta "K2" en posición 1
// Resultado: ["K1", "K2", "Q1", "F1"]
List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Acceso por índice
string primero = componentes[0]; // "K1"
string segundo = componentes[1]; // "Q1"
// Count: número de elementos
int total = componentes.Count; // 4
// Contains: verificar si existe
bool existe = componentes.Contains("K1"); // true
List<string> componentes = new List<string> {"K1", "Q1", "F1", "M1"};
// Remove: elimina la primera ocurrencia
componentes.Remove("Q1");
// Resultado: ["K1", "F1", "M1"]
// RemoveAt: elimina por índice
componentes.RemoveAt(0);
// Resultado: ["F1", "M1"]
// Clear: elimina todos
componentes.Clear();
// Resultado: []
List<string> componentes = new List<string> {"K1", "K2", "Q1", "Q2", "F1", "M1"};
// IndexOf: encontrar posición
int indice = componentes.IndexOf("Q1"); // 2
if (indice != -1)
{
MessageBox.Show("Encontrado en posición: " + indice);
}
// Find: buscar con condición
string primerK = componentes.Find(c => c.StartsWith("K"));
MessageBox.Show("Primer contactor: " + primerK); // "K1"
// FindAll: buscar todos que cumplan condición
List<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 componentes
componentes.Add("K1");
componentes.Add("K2");
componentes.Add("Q1");
componentes.Add("F1");
// Mostrar todos
string lista = "Componentes:\n";
foreach (string comp in componentes)
{
lista += "- " + comp + "\n";
}
MessageBox.Show(lista);
// Buscar si existe un componente
if (componentes.Contains("Q1"))
{
MessageBox.Show("Guardamotor Q1 encontrado");
}
// Contar contactores
int numContactores = componentes.FindAll(c => c.StartsWith("K")).Count;
MessageBox.Show("Número de contactores: " + numContactores); // 2
// Eliminar componente
componentes.Remove("F1");
MessageBox.Show("Componentes después de eliminar F1: " + componentes.Count); // 3

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

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
// Sintaxis
Dictionary<tipoClave, tipoValor> nombreDict = new Dictionary<tipoClave, tipoValor>();
// Ejemplos
Dictionary<string, double> corrientes = new Dictionary<string, double>();
Dictionary<string, string> fabricantes = new Dictionary<string, string>();
Dictionary<int, string> componentes = new Dictionary<int, string>();
Dictionary<string, double> corrientes = new Dictionary<string, double>();
// Add: agregar par clave-valor
corrientes.Add("K1", 25.0);
corrientes.Add("K2", 40.0);
corrientes.Add("Q1", 16.0);
// O inicialización directa
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0},
{"Q1", 16.0}
};
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0},
{"Q1", 16.0}
};
// Acceso directo por clave
double corrienteK1 = corrientes["K1"]; // 25.0
// Verificar si existe una clave
if (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");
}
Dictionary<string, double> corrientes = new Dictionary<string, double>
{
{"K1", 25.0},
{"K2", 40.0}
};
// Modificar valor existente
corrientes["K1"] = 32.0;
// Eliminar entrada
corrientes.Remove("K2");
// Count: número de entradas
int total = corrientes.Count; // 1

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 claves
foreach (string clave in corrientes.Keys)
{
MessageBox.Show(clave);
}
// Solo valores
foreach (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ón
Dictionary<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 componente
string 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 componentes
string 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);

CaracterísticaArrayListDictionary<K,V>
TamañoFijoDinámicoDinámico
AccesoPor índicePor índicePor clave
BúsquedaLenta (O(n))Lenta (O(n))Rápida (O(1))
AgregarNoSí (Add)Sí (Add)
EliminarNoSí (Remove)Sí (Remove)
Uso típicoDatos fijosListas dinámicasBú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

List List<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.

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 componente
public 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);
}
}

El código completo está disponible en code/cap-09/GestorListaMateriales.cs.

Funcionalidades incluidas:

  1. Agregar componentes con validación
  2. Buscar por designación, código, o tipo
  3. Eliminar componentes
  4. Filtrar por corriente, voltaje, fabricante
  5. Estadísticas: Total, corriente total, por tipo
  6. Reportes formateados
  7. Validaciones (duplicados, valores incorrectos)

Array y List: Búsqueda lineal O(n)

// Tiene que revisar todos los elementos
List<string> componentes = new List<string> {"K1", "K2", "Q1", "F1"};
bool existe = componentes.Contains("F1"); // O(n) - revisa 4 elementos

Dictionary<K,V>: Búsqueda por hash O(1)

// Acceso directo mediante hash
Dictionary<string, string> componentes = new Dictionary<string, string>
{
{"K1", "Contactor"},
{"Q1", "Guardamotor"}
};
bool existe = componentes.ContainsKey("K1"); // O(1) - acceso directo

Conclusión: Para búsquedas frecuentes, usa Dictionary.

Puedes combinar colecciones:

// Dictionary de Lists
Dictionary<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"}}
};
// Acceso
List<string> contactores = componentesPorTipo["Contactor"];
foreach (string contactor in contactores)
{
MessageBox.Show(contactor);
}

LINQ (Language Integrated Query) permite consultas poderosas:

List<Componente> componentes = ObtenerComponentes();
// Filtrar por corriente > 20A
var altaCorriente = componentes.Where(c => c.Corriente > 20.0).ToList();
// Ordenar por corriente
var ordenados = componentes.OrderBy(c => c.Corriente).ToList();
// Obtener solo designaciones
var designaciones = componentes.Select(c => c.Designacion).ToList();
// Suma total de corrientes
double corrienteTotal = componentes.Sum(c => c.Corriente);
// Corriente promedio
double corrientePromedio = componentes.Average(c => c.Corriente);

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)

Capítulo 8: Funciones (Métodos)

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.



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 es mejor porque es más flexible.

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ón
foreach (var item in lista)
{
lista.Remove(item); // ❌ ERROR
}
// Bien - usar for inverso
for (int i = lista.Count - 1; i >= 0; i--)
{
lista.RemoveAt(i); // ✓ OK
}

R: No. Cada clave debe ser única. Si intentas agregar una clave que ya existe, se lanza excepción. Usa ContainsKey() antes de agregar.


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/