Ilustración conceptual de un catálogo de puertas consultado mediante una ficha que representa su clave.

Diccionarios y mapas: encontrar datos mediante una clave

La descripción dice «Puerta de madera». La otra también. Y, sin embargo, tienen códigos distintos. Parece que el nombre elegido para ayudar a los humanos a entender un catálogo no estaba obligado a comportarse como un identificador único. Una falta de consideración que podemos resolver antes de construir una automatización que actualice el primer tipo que le resulte simpático.

Un diccionario relaciona claves con valores y permite consultar un dato a partir de su clave. Vamos a preparar un catálogo ficticio que asocia identificadores de tipo de puerta con códigos, distinguir una clave ausente de un código explícitamente nulo y detectar una entrada duplicada sin sobrescribirla por accidente. Usaremos dict de Python y Dictionary de C#, conservando las diferencias entre sus operaciones. El resultado será una consulta explicable, no una actualización del modelo.

Tabla de contenidos

La clave responde a qué registro te refieres

Una lista permite consultar el elemento de una posición. Un diccionario permite consultar el valor asociado a una clave. Si la pregunta es «¿qué código tiene el tipo T02?», resulta más directo utilizar T02 que recordar en qué fila estaba ayer. La elección de esa clave necesita un acuerdo: qué representa, en qué ámbito es única y si puede cambiar durante el proceso. Un recipiente nuevo no mejora por sí solo la calidad de la etiqueta que metemos dentro.

En nuestro ejercicio T01 y T02 son identificadores ficticios de tipos dentro de un único catálogo. Sus códigos son D01 y D02. Dos tipos podrían compartir descripción sin compartir identificador; también podrían compartir un código si el criterio del proyecto lo permitiera. La unicidad del diccionario se exige a las claves. Los valores no tienen que ser diferentes entre sí, y convertirlos automáticamente en claves invertiría la relación que queremos consultar.

En Python escribimos pares clave y valor dentro de llaves, con dos puntos entre ambos. Este primer programa consulta T02 y muestra el número de asociaciones. Puedes ejecutarlo como un archivo .py con Python 3.12; no necesita paquetes adicionales.

				
					codigos = {"T01": "D01", "T02": "D02"}
print(codigos["T02"])
print(len(codigos))
				
			

La salida es D02 y 2. El texto entre corchetes es aquí una clave, no una posición. Si cambiamos la forma de presentar el catálogo, T02 sigue siendo la pregunta que queremos hacer. Esto tampoco demuestra que T02 sea una clave válida para cualquier fuente BIM: en una integración real habría que conservar el ámbito del identificador, como el documento al que pertenece. Yo no confiaría la identidad entre proyectos a cuatro caracteres que acabamos de inventar para una explicación.

La estructura evita tener que escribir un recorrido de búsqueda cada vez que consultamos una clave, aunque no conviene convertir esa comodidad en una promesa de rendimiento universal. El tamaño, el tipo de clave y el trabajo que rodea a la consulta siguen importando. En este primer caso la ventaja principal es que la pregunta queda escrita de forma reconocible y podemos definir qué significa no encontrarla.

Ausente y nulo necesitan diagnósticos distintos

Ahora añadimos T03 con valor None: el registro existe en el catálogo, pero no tenemos un código asignado. T99, en cambio, no existe. Si una herramienta debe preparar un informe, esas situaciones piden actuaciones diferentes. Quizá T03 requiera completar un dato y T99 revisar la correspondencia entre fuentes. Mezclarlas en un único «sin resultado» produce un informe breve a costa de enviarle la investigación al siguiente humano. Una externalización del trabajo bastante eficiente para el script.

En Python, consultar con corchetes una clave ausente provoca KeyError. El método get puede devolver un valor alternativo, y sin alternativa explícita devuelve None cuando no encuentra la clave. Por eso get por sí solo no distingue nuestro T03 de T99. La referencia de diccionarios de Python documenta tanto el acceso por clave como estas operaciones de consulta.

Aquí comprobaremos primero la pertenencia mediante in. Cuando la clave existe, leeremos su valor y decidiremos si es nulo. El orden de los controles expresa el diagnóstico y protege la consulta con corchetes. El texto vacío sería otro valor distinto; no hemos decidido equipararlo al nulo.

				
					codigos = {"T01": "D01", "T02": "D02", "T03": None}
for tipo in ["T02", "T03", "T99"]:
    if tipo not in codigos:
        print(tipo, "ausente")
    elif codigos[tipo] is None:
        print(tipo, "sin codigo")
    else:
        print(tipo, codigos[tipo])
				
			

Obtendrás T02 D02, T03 sin codigo y T99 ausente. El diagnóstico no depende de si el valor «parece verdadero». Una condición como if codigos.get(tipo) combinaría ausencia, nulo y cadena vacía en una misma rama. Puede ser adecuada para otra pregunta, pero no para la que acabamos de acordar. Una línea más corta no merece que el informe pierda precisamente la distinción que tenía que explicar.

Tampoco se debería interpretar None como autorización para escribir una cadena vacía en el modelo. La consulta describe el estado de un dato; la política de corrección viene después. En este ejercicio no hay ninguna escritura que realizar. Podemos revisar la salida antes de decidir cómo tratar cada caso en una herramienta con efectos reales.

Una clave repetida obliga a escoger una política

Al construir el catálogo desde filas aparece otra situación: T02 puede llegar dos veces. La asignación codigos[tipo] = codigo establece el valor de esa clave; si ya existía, lo reemplaza. En Python, un literal con claves repetidas también acaba conservando el último valor correspondiente. Eso puede ser justo lo que quieres para una actualización, pero es una mala forma de detectar conflictos en una importación: el desacuerdo desaparece antes de que lo revise nadie.

Elegiremos una política conservadora para preparar el catálogo: conservar la primera asociación, registrar cualquier clave repetida y considerar el conjunto pendiente de revisión si aparece alguna. No aplicaremos ese catálogo a datos del modelo. Incluso dos filas idénticas se registrarán como repetición; decidir que pueden fusionarse sin aviso sería otra política que tendríamos que dejar escrita.

				
					filas = [("T01", "D01"), ("T02", "D02"), ("T02", "X99"), ("T03", None)]
codigos = {}
duplicadas = []
for tipo, codigo in filas:
    if tipo in codigos:
        duplicadas.append(tipo)
    else:
        codigos[tipo] = codigo

print("Duplicadas:", ", ".join(duplicadas))
print("T02:", codigos["T02"])
print("Estado:", "revisar" if len(duplicadas) > 0 else "listo")
				
			

La salida muestra Duplicadas: T02, T02: D02 y Estado: revisar. El valor X99 no reemplaza a D02, pero tampoco fingimos que el conflicto se ha resuelto. Conservar el primero sirve para no perder la asociación inicial durante el diagnóstico; no convierte al primero en el correcto por antigüedad. El orden de llegada no debería recibir el cargo de responsable de calidad sin que nadie lo haya nombrado.

Los nombres de las variables también ayudan a entender la política. filas conserva las entradas completas y codigos contiene las asociaciones aceptadas por esta primera construcción. Si después necesitamos explicar qué dos valores chocaron y en qué filas, ampliaremos el registro del conflicto. Para mostrar el mecanismo basta con detectar la clave y mantener los datos originales disponibles, en lugar de destruirlos al crear el diccionario.

Dictionary ofrece operaciones con decisiones diferentes

En C#, Dictionary<string, string?> declara claves de texto y valores de texto que también pueden ser nulos. Utilizaremos StringComparer.Ordinal para comparar nuestras claves exactamente, distinguiendo mayúsculas y minúsculas. La elección es deliberada: T02 y t02 serán claves distintas. Si el sistema de origen define otra equivalencia, hay que acordarla antes de introducir una normalización que pueda fusionar registros.

Para construir el catálogo usaremos TryAdd. Devuelve falso cuando la clave ya existe y conserva el valor anterior, lo que nos permite registrar el conflicto. Add genera una excepción ante una clave duplicada y la asignación mediante el indexador reemplaza el valor. Son operaciones diferentes dentro de la misma estructura. Tener un diccionario no implica haber escogido cómo tratar las repeticiones.

				
					using System;
using System.Collections.Generic;

var filas = new (string Tipo, string? Codigo)[]
{
    ("T01", "D01"), ("T02", "D02"),
    ("T02", "X99"), ("T03", null)
};
var codigos = new Dictionary<string, string?>(StringComparer.Ordinal);
var duplicadas = new List<string>();
foreach (var fila in filas)
{
    if (!codigos.TryAdd(fila.Tipo, fila.Codigo))
        duplicadas.Add(fila.Tipo);
}
Console.WriteLine($"Duplicadas: {string.Join(", ", duplicadas)}");
Console.WriteLine($"T02: {codigos["T02"]}");
Console.WriteLine($"Estado: {(duplicadas.Count > 0 ? "revisar" : "listo")}");

foreach (string tipo in new[] { "T02", "T03", "T99" })
{
    if (!codigos.TryGetValue(tipo, out string? codigo))
        Console.WriteLine($"{tipo} ausente");
    else if (codigo is null)
        Console.WriteLine($"{tipo} sin codigo");
    else
        Console.WriteLine($"{tipo} {codigo}");
}
				
			

Este programa completo de consola para .NET 8 reproduce los tres mensajes de construcción y después los tres diagnósticos de consulta. TryGetValue devuelve una respuesta booleana que indica si encontró la clave y entrega el valor mediante out. Comprobar solo si codigo es nulo volvería a mezclar los dos casos: aquí la respuesta booleana conserva la información de existencia.

La referencia de Dictionary de .NET reúne estas operaciones y sus contratos. Los ejemplos comparan resultados con Python, sin afirmar que ambos tipos tengan idéntico comportamiento en todas sus variantes. Para consultar una clave de este catálogo importa que la política de igualdad y la de inserción sean explícitas, no que los nombres de los métodos se parezcan entre lenguajes.

Antes de cruzar datos, comprueba el contrato de la clave

Ya podemos consultar T02, reconocer el registro incompleto T03 y distinguir el desconocido T99. También sabemos que el catálogo con T02 repetido requiere revisión. Esas cuatro situaciones forman una primera prueba mucho más útil que consultar únicamente T01 y celebrar que devuelve D01. Un ejemplo amable puede demostrar que hemos escrito la sintaxis; un conflicto obliga a comprobar qué decisión hemos programado.

Prueba ahora dos filas idénticas de T02: nuestra política seguirá marcando repetición. Después usa t02 en minúscula: los ejemplos lo tratarán como otra clave. Por último, sustituye el nulo de T03 por ""; dejará de mostrarse como nulo, porque no hemos añadido una regla de vacío. Si ese comportamiento no corresponde a tu proyecto, cambia el contrato y sus pruebas antes de cambiar una condición a escondidas.

Al preparar una integración, la pregunta incómoda es qué representa realmente su clave. ¿Un tipo dentro de un documento, un elemento entre exportaciones, un código acordado por el equipo? Una descripción visible puede servir para mostrar el registro y resultar pésima para relacionarlo. Puedes contar en los comentarios qué identificador te ha dado problemas al conectar datos; el detalle útil es qué cambió o dejó de ser único, no el nombre de la aplicación a la que decidimos echarle la culpa.

Un diccionario nos permite expresar la relación que hemos definido. No certifica que esa relación tenga sentido, y desde luego no convierte una coincidencia de texto en permiso para actualizar un modelo. Con una clave bien elegida y los conflictos visibles, el siguiente cruce de tablas tendrá una base comprobable. Sin eso solo habremos sustituido una búsqueda confusa por una búsqueda confusa que responde más deprisa.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *