Filtrar, transformar y agrupar colecciones: tres operaciones distintas
Un informe puede tener menos filas que la entrada por dos motivos muy diferentes: has descartado registros o has reunido varios bajo una misma etiqueta. También puede tener las mismas filas y haber perdido justo el campo que permitía encontrar el objeto. Llamarlo todo «procesar datos» ayuda a terminar la explicación antes, pero deja al siguiente programador una interesante colección de preguntas. Y, por alguna razón, el siguiente programador suele ser la misma persona tres semanas después.
Vamos a separar tres operaciones con un inventario pequeño: seleccionar puertas sin código, convertirlas en registros de incidencia y reunir esas incidencias por nivel. Haremos el recorrido completo en Python y C#, conservando identificadores y comprobando que la agrupación no se come ninguna fila. No necesitamos conectar Revit para entender esta parte: trabajaremos con registros en memoria y una regla deliberadamente estrecha. Después podrás reconocer la misma estructura al tratar una exportación o la respuesta de una API.
Elegir, cambiar de forma y reunir no hacen lo mismo
Filtrar significa decidir qué registros siguen adelante. Una condición produce una respuesta para cada entrada: entra o no entra. En nuestro caso seleccionaremos únicamente los registros cuya categoría sea exactamente Puerta y cuyo código sea exactamente la cadena vacía. Una ventana sin código queda fuera porque está fuera del alcance; una puerta con código queda fuera porque no cumple esta condición de incidencia. Ninguna de las dos ha desaparecido por accidente si esa era la regla anunciada.
Transformar significa construir otra representación de los registros seleccionados. De cada puerta obtendremos una incidencia con identificador, nivel y motivo. El código y la categoría ya no aparecerán en esa representación porque han servido para tomar la decisión anterior. El identificador sí se conserva: un informe que explica muy bien un fallo pero no permite localizarlo ha automatizado la parte menos urgente del problema. La transformación de este ejemplo produce una incidencia por cada registro seleccionado; otras transformaciones pueden tener reglas distintas, así que no conviertas esa relación en una propiedad universal.
Agrupar significa reunir los registros que comparten una clave. Pondremos juntas las incidencias cuyo nivel coincida. Eso no equivale todavía a resumirlas: cada grupo conservará sus incidencias completas. Solo al preparar la salida calcularemos cuántas contiene y mostraremos sus identificadores. Esta separación permite pasar del resumen al detalle sin volver a ejecutar la selección. El número total será tres, pero saber que hay tres problemas no indica dónde corregirlos.
El contrato de entrada importa tanto como las operaciones. Cada registro tendrá cuatro campos de texto: identificador, categoría, nivel y código. Usaremos niveles no vacíos y etiquetas estables, como N00 y N01. No estamos validando aquí todo ese esquema ni comprobando un catálogo de códigos: un espacio, por ejemplo, no es la cadena vacía y no se seleccionará. Si tu regla real considera ambos casos equivalentes, tendrá que decirlo y probarlo. Puedes apoyarte en la explicación de diccionarios y mapas para repasar las estructuras; aquí nos interesa qué se conserva al pasar de una a otra.
Python: dejar visibles las tres etapas
Guarda este programa como incidencias_por_nivel.py. Utiliza Python 3.10 o posterior y no necesita paquetes externos. Las dos comprensiones construyen listas nuevas; el bucle siguiente forma un diccionario de grupos. Cada valor de ese diccionario es una lista de incidencias, no únicamente un contador.
Ejecuta el archivo desde su carpeta:
La primera línea debe decir Seleccionadas: 3. Después aparecerán N00 | 2 | P01, P04 y N01 | 1 | P03. Puedes seguir cada resultado hasta la entrada: P02 y P05 tenían código; V01 no era una puerta. La suma de tamaños de los grupos es tres y sus identificadores son los mismos que los de la lista de incidencias. Estas dos comprobaciones cuentan cosas distintas: una fila perdida y otra duplicada podrían dejar el total aparentemente correcto.
setdefault devuelve la lista ya asociada a un nivel o crea una vacía si ese nivel todavía no existe. Añadir una incidencia a esa lista no modifica los diccionarios de entrada: la transformación ha creado diccionarios nuevos. Aun así, los valores de este ejemplo son textos; no uses este caso para concluir que copiar una estructura anidada duplica todos sus objetos. La documentación de comprensiones de listas explica cómo construir una colección a partir de otra. Nosotros hemos separado dos expresiones para que la selección y la forma de salida se puedan inspeccionar por separado. Reducir líneas no es una competición que el informe necesite ganar.
C#: Where selecciona, Select transforma y GroupBy reúne
Crea una aplicación de consola con .NET 8:
Sustituye IncidenciasPorNivel/Program.cs por el programa completo siguiente. Las tuplas nombran los cuatro campos de entrada y los tres de salida. Where aplica la condición, Select construye las incidencias y GroupBy las reúne por nivel. El diccionario final conserva un array con el detalle de cada grupo.
Ejecuta el proyecto:
Las tres líneas coinciden con las de Python. Los niveles y los identificadores se ordenan para presentar un informe estable con estas etiquetas ASCII. Agrupar no ha establecido una prioridad de revisión ni una secuencia física de plantas: N00 aparece primero por la ordenación elegida. Si tus niveles se llaman «Baja», «Primera» y «Sótano», el orden alfabético probablemente no sea el que espera quien va a revisar el edificio. Ese orden necesita otra regla, no una interpretación optimista del resultado.
Las operaciones LINQ que devuelven secuencias pueden aplazar su ejecución hasta que se recorren. En este programa utilizamos ToArray para materializar las etapas y conservar sus resultados; no estamos midiendo una ventaja de rendimiento. La documentación de Where, Select y GroupBy permite contrastar qué devuelve cada operación. Unirlas en una sola cadena puede ser razonable cuando conoces sus contratos. Mantenerlas separadas aquí permite señalar exactamente dónde una entrada dejó de estar seleccionada o cambió de representación.
Contar grupos no equivale a contar objetos únicos
Duplica el registro P01 en la entrada y vuelve a ejecutar. Obtendrás cuatro incidencias y tres registros dentro de N00, con P01 dos veces en el detalle. El programa está contando filas seleccionadas: no comprueba que cada identificador aparezca una sola vez. Eso puede representar una duplicidad accidental, dos observaciones del mismo objeto o datos procedentes de modelos distintos. El agrupador no dispone de información para elegir una explicación, y agradecería que no se le ascendiese a coordinador BIM por haber creado un diccionario.
Si necesitas contar objetos únicos, define primero su identidad. Tal vez debas combinar modelo e identificador, rechazar duplicados o conservar varias observaciones bajo un mismo objeto. Aplicar una deduplicación sin esa decisión puede borrar evidencia. Tampoco sería correcto agrupar directamente por identificador si el informe prometía una distribución por nivel: habrías cambiado la pregunta. Conserva la diferencia entre un registro, una incidencia y el objeto al que esa incidencia se refiere.
La comparación de claves también debe ser deliberada. En estos ejemplos N00 y n00 forman grupos distintos; no hay normalización de mayúsculas ni espacios. Si una fuente emplea ambas etiquetas para el mismo nivel, resuelve esa equivalencia antes de agrupar o hazla explícita en una fase independiente. No arregles la presentación convirtiéndolo todo a mayúsculas si esas etiquetas son identificadores cuya comparación tiene otro contrato. Una columna que parece un nombre puede estar cumpliendo la función de una clave.
El resumen tampoco sustituye al detalle. Mostrar «dos incidencias en N00» es útil para distribuir trabajo, pero la persona que corrige necesita P01 y P04 y el motivo de selección. En una herramienta posterior podrías enlazar cada incidencia a su objeto o exportarla; este ejemplo termina en consola. Lo que sí deja preparado es el dato necesario para esa navegación. La estética de la tarjeta con el número dos puede esperar hasta que sepamos qué dos estamos contando.
Comprobar cada etapa evita un informe convincente y equivocado
Empieza por las identidades esperadas: la selección original debe contener P01, P03 y P04, una vez cada una. La transformación debe mantener esos identificadores y asignar el motivo codigo vacio. La agrupación debe reunir P01 y P04 bajo N00 y P03 bajo N01. Finalmente, la suma de tamaños debe coincidir con el número de incidencias. Comprobar solo el total sería aceptar que cualquier combinación de tres registros sirve como respuesta.
Prueba una entrada vacía y otra con todas las puertas codificadas. Ambas producen cero incidencias y ningún grupo en este programa; no se crean grupos vacíos para todos los niveles de un proyecto. Si el informe debe mostrarlos con cero, necesitarás además un catálogo de niveles contra el que completar la salida. Prueba después una ventana sin código: no debe entrar. Es una prueba pequeña, pero detecta una condición de filtrado que revise el código y se olvide de la categoría.
Invierte los registros de entrada. La presentación ordenada debe conservar las mismas líneas, aunque el orden interno de las incidencias cambie. Añade el duplicado comentado antes y comprueba que no desaparece. Conserva también una copia de los datos de entrada para verificar que la función no los ha alterado. Estas pruebas se pueden hacer con registros en memoria y son distintas de comprobar una extracción real: no demuestran que una API haya devuelto todas las páginas o que una exportación incluya todos los modelos necesarios.
La mejora útil es poder explicar qué hace cada etapa y qué evidencia conserva. Filtrar decide el alcance, transformar prepara una representación y agrupar reúne por una clave; el contador llega después. Si tienes un informe que muestra un total pero ya no permite reconstruir sus filas, ahí hay una revisión interesante. Cuenta en los comentarios qué información necesitas conservar para pasar del resumen al objeto que alguien tendrá que corregir: suele ser una pregunta bastante más productiva que discutir qué lenguaje imprime el número con menos líneas.
Respuestas