Go

Go

Tutorial Go: Mapas

Go Mapas: aprende a crear y manipular mapas en Go. Domina mapas anidados, iteración, eliminación de pares y mejora el rendimiento en estructuras de datos.

Aprende Go GRATIS y certifícate

Creación y manipulación de mapas

En Go, los mapas son colecciones desordenadas de pares clave-valor, donde las claves son únicas dentro del mapa. Para crear un mapa, se utiliza la función incorporada make o mediante la declaración literal de mapas.

Por ejemplo, para crear un mapa que asocia cadenas a enteros:

miMapa := make(map[string]int)

También es posible inicializar un mapa con valores utilizando una declaración literal:

otroMapa := map[string]int{
    "clave1": 100,
    "clave2": 200,
}

Una vez creado el mapa, se pueden agregar nuevos pares clave-valor asignándolos directamente:

miMapa["nuevaClave"] = 42

Para recorrer un mapa, se utiliza el bucle for range, que itera sobre las claves y los valores:

for clave, valor := range miMapa {
    fmt.Printf("Clave: %s, Valor: %d\n", clave, valor)
}

Es importante mencionar que el orden de iteración en un mapa es aleatorio, ya que los mapas en Go no mantienen un orden específico. Esto significa que cada vez que se recorre un mapa, el orden de los elementos puede variar.

Los mapas pueden ser anidados, permitiendo estructuras más complejas. Por ejemplo, un mapa cuya clave es una cadena y cuyo valor es otro mapa:

mapaAnidado := make(map[string]map[string]int)
mapaAnidado["categoría"] = make(map[string]int)
mapaAnidado["categoría"]["subclave"] = 1

Para mejorar el rendimiento y evitar asignaciones innecesarias, es posible especificar la capacidad inicial del mapa al crearlo:

mapaConCapacidad := make(map[string]int, 100)

Esto inicializa un mapa con una capacidad estimada para 100 elementos, lo que puede ser útil cuando se conoce de antemano el tamaño aproximado del mapa.

Los mapas también pueden interactuar con otras estructuras de datos. Por ejemplo, para convertir un mapa en una lista de claves o valores:

var claves []string
for clave := range miMapa {
    claves = append(claves, clave)
}

La comparación directa entre mapas no está permitida en Go. No se pueden comparar dos mapas utilizando el operador ==, excepto si se compara con nil. Para verificar si dos mapas son iguales, es necesario comparar sus contenidos de forma manual.

Finalmente, es posible eliminar todos los elementos de un mapa de forma eficiente asignando un nuevo mapa vacío a la misma variable:

miMapa = make(map[string]int)

Esto libera la memoria asociada al mapa anterior y proporciona un mapa vacío para su reutilización.

Acceso y modificación de elementos

Para acceder a un elemento en un mapa, se utiliza la clave entre corchetes después del nombre del mapa. Por ejemplo, si tenemos un mapa miMapa y queremos obtener el valor asociado a la clave "clave1":

valor := miMapa["clave1"]
fmt.Println(valor)

Si la clave no existe en el mapa, se devolverá el valor cero del tipo de los valores del mapa. Por ejemplo, si los valores son enteros, obtendremos 0.

Para modificar el valor asociado a una clave específica, simplemente se asigna un nuevo valor utilizando la misma sintaxis:

miMapa["clave1"] = 42

Esta operación actualizará el valor de "clave1" a 42. Si la clave no existía previamente en el mapa, se creará un nuevo par clave-valor.

Es común querer saber si una clave existe en el mapa antes de realizar alguna acción. Para ello, se emplea el idioma coma, ok:

valor, existe := miMapa["clave2"]
if existe {
    fmt.Printf("El valor es %d\n", valor)
} else {
    fmt.Println("La clave no existe en el mapa")
}

En este caso, la variable existe será true si la clave "clave2" está presente en el mapa, y false en caso contrario.

Al trabajar con mapas, es importante tener en cuenta que los valores son referencia directa al mapa original. Esto significa que si se pasa un mapa a una función y se modifica dentro de ella, los cambios serán visibles fuera de la función.

func agregarElemento(m map[string]int, clave string, valor int) {
    m[clave] = valor
}

agregarElemento(miMapa, "nuevaClave", 100)

Después de llamar a agregarElemento, el mapa miMapa contendrá el nuevo par clave-valor.

Para iterar y acceder a los elementos de un mapa, se utiliza el bucle for range:

for clave, valor := range miMapa {
    fmt.Printf("Clave: %s, Valor: %d\n", clave, valor)
}

Esta estructura permite recorrer todos los pares clave-valor presentes en el mapa.

Si se necesita actualizar todos los valores del mapa basándose en cierta lógica, se puede hacer dentro del bucle:

for clave := range miMapa {
    miMapa[clave] *= 2
}

Este código duplica cada uno de los valores asociados a las claves en el mapa.

Es posible también acceder parcialmente a los elementos del mapa. Si solo interesa la clave o el valor, se puede omitir el otro utilizando el identificador de blanco _:

for _, valor := range miMapa {
    fmt.Println(valor)
}

Aquí, solo se imprimen los valores, ignorando las claves.

Cuando se trabaja con mapas cuyos valores son tipos complejos, como estructuras, es crucial recordar que las estructuras se copian por valor. Por lo tanto, para modificar los campos de una estructura almacenada en un mapa, se debe trabajar con punteros o reasignar el valor completo.

type Persona struct {
    Nombre string
    Edad   int
}
mapaPersonas := make(map[string]Persona)
mapaPersonas["ID123"] = Persona{"Alice", 30}

// Intento de modificar la edad (no funcionará)
persona := mapaPersonas["ID123"]
persona.Edad = 31

// La edad sigue siendo 30
fmt.Println(mapaPersonas["ID123"].Edad)

Para que la modificación tenga efecto, se debe reasignar el valor al mapa:

persona := mapaPersonas["ID123"]
persona.Edad = 31
mapaPersonas["ID123"] = persona

Alternativamente, se puede utilizar un mapa de punteros:

mapaPersonasPtr := make(map[string]*Persona)
mapaPersonasPtr["ID123"] = &Persona{"Bob", 25}

// Modificación directa
mapaPersonasPtr["ID123"].Edad = 26
fmt.Println(mapaPersonasPtr["ID123"].Edad)

Al usar punteros, los cambios en los campos de la estructura se reflejan directamente en el mapa.

Detección de claves y eliminación de pares

Al trabajar con mapas en Go, es fundamental saber cómo detectar si una clave existe y cómo eliminar pares clave-valor. Aunque acceder a un valor en un mapa es sencillo, es importante manejar correctamente los casos en los que una clave puede no estar presente.

Para verificar si una clave existe en un mapa, se utiliza la sintaxis de asignación con el segundo valor de retorno. Este método permite diferenciar entre un valor cero y la ausencia de una clave:

valor, existe := miMapa["claveEjemplo"]
if existe {
    fmt.Printf("El valor asociado es %v\n", valor)
} else {
    fmt.Println("La clave no existe en el mapa")
}

En este caso, existe es un valor booleano que será true si la clave "claveEjemplo" está presente, y false en caso contrario.

Es relevante destacar que, incluso si el valor asociado a una clave es el valor cero del tipo del mapa, este método permite distinguir entre una clave existente con valor cero y una clave inexistente. Esto es especialmente útil en tipos numéricos o booleanos donde el valor cero puede ser significativo.

Para eliminar un par clave-valor de un mapa, Go proporciona la función incorporada delete. Esta función toma como argumentos el mapa y la clave que se desea eliminar:

delete(miMapa, "claveEjemplo")

Después de ejecutar esta instrucción, la clave "claveEjemplo" y su valor asociado ya no estarán presentes en miMapa.

Si se intenta eliminar una clave que no existe en el mapa, no ocurre ningún error. La función delete simplemente no hace nada en este caso, lo cual facilita su uso sin necesidad de comprobaciones previas.

La eliminación de pares clave-valor puede ser útil para gestionar la memoria y los recursos cuando se manipulan mapas grandes o de larga duración. Al eliminar entradas innecesarias, se evita el consumo innecesario de memoria y se mantiene el mapa optimizado.

Es posible eliminar múltiples claves en una iteración si se cumplen ciertas condiciones. Por ejemplo, para eliminar todas las claves cuyos valores cumplen un criterio específico:

for clave, valor := range miMapa {
    if debeEliminarse(valor) {
        delete(miMapa, clave)
    }
}

En este fragmento, la función debeEliminarse determina si un valor cumple con el criterio para ser eliminado.

Es fundamental tener en cuenta que no se debe modificar la longitud de un mapa mientras se está iterando sobre él, excepto mediante la función delete. Agregar nuevos pares clave-valor dentro de una iteración puede llevar a comportamientos inesperados.

Además, es posible utilizar la detección de claves para implementaciones condicionales. Por ejemplo, se puede establecer un valor predeterminado si una clave no existe:

if _, existe := miMapa["clavePorDefecto"]; !existe {
    miMapa["clavePorDefecto"] = valorPredeterminado
}

Este enfoque garantiza que el mapa siempre contenga una clave con un valor válido, evitando comprobaciones posteriores y posibles errores en tiempo de ejecución.

Por último, es importante recordar que los mapas en Go son referencias a estructuras internas. Por lo tanto, pasar un mapa a una función y modificarlo dentro de ella afectará al mapa original. Esto incluye tanto la adición como la eliminación de pares clave-valor.

Aprende Go GRATIS online

Todas las lecciones de Go

Accede a todas las lecciones de Go y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Go y certifícate

Certificados de superación de Go

Supera todos los ejercicios de programación del curso de Go y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  • Crear mapas en Go utilizando make y literales de mapa.
  • Acceder y modificar elementos en mapas.
  • Verificar la existencia de claves con el idioma coma, ok.
  • Iterar sobre mapas usando bucles for range.
  • Implementar y manipular mapas anidados.
  • Eliminar pares clave-valor utilizando la función delete.
  • Optimizar el rendimiento especificando la capacidad inicial de mapas.
  • Entender la no comparabilidad directa de mapas.
  • Gestionar referencias al pasar mapas a funciones.
  • Modificar estructuras almacenadas en mapas y comprender el comportamiento por valor y por referencia.