La mayoría de los lenguajes de programación modernos incluyen el concepto de tipo de diccionario o de hash. Estos tipos se utilizan comúnmente para almacenar datos en pares con una clave que se asigna a un valor.

En Go, el tipo de datos de mapa es lo que la mayoría de los programadores conoce como el tipo de diccionario. Asigna claves a valores y crea pares clave-valor que son una alternativa útil para almacenar datos en Go. Un mapa se construye usando la palabra clave map; a esta le sigue el tipo de datos de clave entre corchetes [ ] y a esto le sigue el tipo de datos de valor. Los pares clave-valor posteriormente se disponen dentro de llaves { }:

map[key]value{}

Normalmente, los mapas se usan en Go para almacenar datos relacionados, como la información contenida en un ID. Un mapa con datos tiene el siguiente aspecto:

map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

Además de las llaves, también hay dos puntos a lo largo del mapa que conectan los pares clave-valor. Las palabras a la izquierda de los dos puntos son las claves. Para las claves se puede usar cualquier tipo _comparable _en Go, como strings e ints, entre otros.

Las claves del mapa de ejemplo son las siguientes:

  • “name”
  • “animal”
  • “color”
  • “location”

Las palabras que se sitúan a la derecha de los dos puntos son los valores. Para los valores se puede usar cualquier tipo de datos. Los valores del mapa de ejemplo son los siguientes:

  • “Sammy”
  • “shark”
  • “blue”
  • “ocean”

Al igual que los otros tipos de datos, puede almacenar el mapa dentro de una variable e imprimirlo:

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
fmt.Println(sammy)

Este sería su resultado:

Output
map[animal:shark color:blue location:ocean name:Sammy]

Es posible que el orden de los pares clave-valor haya cambiado. En Go, el tipo de datos de mapa no está ordenado. Independientemente del orden, los pares clave-valor se mantendrán intactos, lo que le permitirá acceder a datos según su significado relacional.

Acceder a elementos de mapas

Puede invocar los valores de un mapa haciendo referencia a las claves relacionadas. Debido a que los mapas ofrecen pares clave-valor para el almacenamiento de datos, pueden ser elementos importantes y útiles en su programa de Go.

Si quiere aislar el nombre de usuario de Sammy, puede hacerlo invocando sammy[“name”], la variable que contiene su mapa y la clave relacionada. Lo imprimiremos:

fmt.Println(sammy["name"])

Recibiremos el valor como resultado:

Output
Sammy

Los mapas se comportan como una base de datos; en lugar de invocar a un entero para obtener un valor de índice específico como lo haría con un segmento, asignará un valor a una clave e invocará esa clave para obtener su valor relacionado.

Al invocar la clave “name”, recibirá el valor de esa clave, que es “Sammy”.

De igual manera, puede invocar los valores restantes en el mapa sammy usando el mismo formato:

fmt.Println(sammy["animal"])
// returns shark

fmt.Println(sammy["color"])
// returns blue

fmt.Println(sammy["location"])
// returns ocean

Usando los pares clave-valor en los tipos de datos de mapa, puede hacer referencia a las claves para obtener valores.

Claves y valores

A diferencia de algunos lenguajes de programación, Go no cuenta con funciones de conveniencia para enumerar las claves o los valores de un mapa. Un ejemplo de esto sería el método .keys() de Python para diccionarios. Sin embargo, se permite la iteración usando el operador range:

for key, value := range sammy {
    fmt.Printf("%q is the key for the value %q\n", key, value)
}

Durante el desplazamiento por un mapa en Go, se mostrarán dos valores. El primer valor representará la clave y el segundo el valor. En Go, se crearán estas variables con el tipo de datos correcto. En este caso, la clave del mapa fue una string, por lo que key también será una cadena. El value también es una cadena:

Output
"animal" is the key for the value "shark" "color" is the key for the value "blue" "location" is the key for the value "ocean" "name" is the key for the value "Sammy"

Para obtener una lista que contenga únicamente las claves, puede usar el operador range de nuevo. Puede declarar una sola variable para acceder únicamente a las claves:

keys := []string{}

for key := range sammy {
    keys = append(keys, key)
}
fmt.Printf("%q", keys)

En el programa primero declara un segmento en el que almacenará sus claves.

En el resultado solo se mostrarán las claves de su mapa:

Output
["color" "location" "name" "animal"]

Una vez más, las claves no están ordenadas. Si desea ordenarlas, utilice la función sort.Strings del paquete sort:

sort.Strings(keys)

Con esta función, obtendrá el siguiente resultado:

Output
["animal" "color" "location" "name"]

Puede usar el mismo patrón para recuperar solo los valores en un mapa. En el siguiente ejemplo, se asigna previamente el segmento para evitar asignaciones, lo cual aporta más eficiencia al programa:

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

items := make([]string, len(sammy))

var i int

for _, v := range sammy {
    items[i] = v
    i++
}
fmt.Printf("%q", items)

Primero, declare un segmento para almacenar sus claves. Debido a que sabe cuántos elementos necesita, puede evitar las posibles asignaciones de memoria definiendo el segmento con el tamaño exacto. Luego declare su variable de índice. Puesto que no desea la clave, utilice el operador _ al iniciar su bucle para ignorar el valor de la clave. Su resultado sería el siguiente:

Output
["ocean" "Sammy" "shark" "blue"]

Para determinar el número de elementos de un mapa, puede usar la función integrada len:

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
fmt.Println(len(sammy))

En el resultado, se muestra el número de elementos de su mapa:

Output
4

Aunque Go no incluye funciones de conveniencia para obtener claves y valores, solo se necesitan algunas líneas de código para recuperar las claves y los valores cuando sea necesario.

Verificar la existencia

En los mapas de Go se mostrará el valor cero para el tipo de valor del mapa cuando falte la clave solicitada. Debido a esto, necesita una alternativa para diferenciar un cero almacenado, en comparación con una clave faltante.

Buscaremos un valor en un mapa que sabemos que no existe y veremos el valor mostrado:

counts := map[string]int{}
fmt.Println(counts["sammy"])

Verá el siguiente resultado:

Output
0

Aunque la clave sammy no se encontraba en el mapa, se mostró el valor 0 en Go. Esto se debe a que el tipo de dato de valor es un int y puesto que en Go se fija un valor de cero para todas las variables, se mostrará el valor cero 0.

En muchos casos, esto no es conveniente y produciría un error en su programa. Al buscar el valor en un mapa, en Go se puede mostrar un segundo valor opcional. Este segundo valor es un bool y será true si se encontró la clave o false si no se encontró la clave. En Go, esto se conoce como la expresión habitual ok. Aunque podría dar el nombre que desee a la variable en la que se captura el segundo argumento, en Go siempre recibe el nombre ok:

count, ok := counts["sammy"]

Si la clave sammy existe en el mapa counts, ok será true. De lo contrario, ok será “false”.

Puede usar la variable ok para decidir lo que hará en su programa:

if ok {
    fmt.Printf("Sammy has a count of %d\n", count)
} else {
    fmt.Println("Sammy was not found")
}

Esto daría como resultado lo siguiente:

Output
Sammy was not found

En Go, puede combinar la declaración de variables y la verificación condicional con un bloque if/else. Esto le permite usar una sola instrucción para dicha verificación:

if count, ok := counts["sammy"]; ok {
    fmt.Printf("Sammy has a count of %d\n", count)
} else {
    fmt.Println("Sammy was not found")
}

Cuando se obtiene un valor de un mapa en Go, siempre es recomendable verificar su existencia también para evitar errores en su programa.

Modificar mapas

Los mapas son una estructura de datos mutable, por lo cual puede modificarlos. En esta sección, veremos la adición y eliminación de elementos en un mapa.

Añadir y cambiar elementos en un mapa

Sin usar un método o una función, puede añadir pares clave-valor a los mapas. Esto se hace usando el nombre de variable de mapas, seguido del valor de clave entre corchetes [ ], y el operador igual = para establecer un nuevo valor:

map[key] = value

En la práctica, puede ver la manera en que esto funciona agregando un par clave -valor a un mapa llamado usernames:

usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}

usernames["Drew"] = "squidly"
fmt.Println(usernames)

En el resultado, se mostrará el nuevo par clave-valor Drew:squidly en el mapa:

Output
map[Drew:squidly Jamie:mantisshrimp54 Sammy:sammy-shark]

Debido a que los mapas se muestran sin orden, este par puede aparecer en cualquier lugar en el resultado del mapa. Si utiliza el mapa usernames más adelante en el archivo de su programa, incluirá el par clave -valor adicional.

También puede usar esta sintaxis para modificar el valor asignado a una clave. En este caso, se hace referencia a una clave existente y se le pasará un valor diferente.

Consideremos un mapa llamado followers en el que se realice un control de los seguidores de los usuarios de una red determinada. Los seguidores del usuario “drew” aumentaron hoy, por lo que debe actualizar el valor entero pasado a la clave “drew”. Usará la función Println() para verificar que el mapa se modificó:

followers := map[string]int{"drew": 305, "mary": 428, "cindy": 918}
followers["drew"] = 342
fmt.Println(followers)

Su resultado mostrará el valor actualizado para drew:

Output
map[cindy:918 drew:342 mary:428]

Verá que el número de seguidores aumentó del valor entero 305 a 342.

Puede usar este método para añadir pares clave-valor a mapas con entradas del usuario. Escribiremos un programa simple llamado usernames.go que se ejecuta en la línea de comandos y permite la entrada del usuario para añadir más nombres y nombres de usuarios asociados:

usernames.go
package main

import (
    "fmt"
    "strings"
)

func main() {
    usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}

    for {
        fmt.Println("Enter a name:")

        var name string
        _, err := fmt.Scanln(&name)

        if err != nil {
            panic(err)
        }

        name = strings.TrimSpace(name)

        if u, ok := usernames[name]; ok {
            fmt.Printf("%q is the username of %q\n", u, name)
            continue
        }

        fmt.Printf("I don't have %v's username, what is it?\n", name)

        var username string
        _, err = fmt.Scanln(&username)

        if err != nil {
            panic(err)
        }

        username = strings.TrimSpace(username)

        usernames[name] = username

        fmt.Println("Data updated.")
    }
}

En usernames.go, primero se define el mapa original. Luego, se configura un bucle para iterar los nombres. Se solicita al usuario ingresar un nombre y declarar una variable para almacenarlo. A continuación, se verifica si se produce un error; si esto sucedió, el programa se cerrará con un panic. Debido a que en Scanln se captura toda la entrada, incluido el retorno de carro, debe eliminar cualquier espacio de la entrada; esto se realiza con la función strings.TrimSpace.

El bloque if verifica si el nombre está presente en el mapa e imprime comentarios. Si el nombre está presente, entonces regresa al principio del bucle. Si el nombre no se encuentra en el mapa, se proporcionan comentarios al usuario y luego se solicita un nuevo nombre de usuario para el nombre asociado. El programa verifica nuevamente si existe un error. Si no hay errores, se elimina el retorno de carro, se asigna el valor del nombre de usuario a la clave de nombre y luego se imprimen comentarios que indican que los datos se actualizaron.

Ejecutaremos el programa en la línea de comandos:

  • go run usernames.go

Verá el siguiente resultado:

Output
Enter a name: Sammy "sammy-shark" is the username of "Sammy" Enter a name: Jesse I don't have Jesse's username, what is it? JOctopus Data updated. Enter a name:

Cuando termine de realizar pruebas, presione CTRL + C para salir del programa.

Esto le muestra la manera de modificar los mapas de forma interactiva. Con este programa en particular, tan pronto como cierre el programa con CTRL + C perderá todos sus datos a menos que implemente una manera de manejar la lectura y escritura de archivos.

En resumen, puede añadir elementos a los mapas o modificar valores con la sintaxis map[key] = value.

Eliminar elementos de mapas

Así como puede añadir pares clave-valor y cambiar valores en el tipo de datos de mapa, también puede eliminar elementos dentro de un mapa.

Para eliminar un par clave-valor de un mapa, puede usar la función integrada delete(). El primer argumento es el mapa desde el cual realiza la eliminación. El segundo argumento es la clave que eliminará:

delete(map, key)

Definiremos un mapa de permisos:

permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16:"modify"}

Ya no necesita el permiso modify. Por lo tanto, lo eliminará de su mapa. Luego imprimirá el mapa para confirmar que se eliminó:

permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16: "modify"}
delete(permissions, 16)
fmt.Println(permissions)

El resultado confirmará la eliminación:

Output
map[1:read 2:write 4:delete 8:create]

A través de la línea delete(permissions, 16) se elimina el par clave -valor 16:"modify" del mapa permissions.

Si quiere eliminar todos los valores de un mapa, puede hacerlo configurándolo de modo que sea igual a un mapa vacío del mismo tipo. Con esto, se creará un nuevo mapa vacío que se podrá utilizar y el recolector de elementos no utilizados quitará el mapa anterior de la memoria.

Eliminaremos todos los elementos del mapa permissions:

permissions = map[int]string{}
fmt.Println(permissions)

En el resultado se muestra que ahora dispone de un mapa vacío de pares clave-valor:

Output
map[]

Debido a que los mapas son tipos de datos mutables, se pueden añadir, modificar, eliminar y quitar elementos de ellos.

Conclusión

En este tutorial, se analizó la estructura de datos de mapas de Go. Los mapas se componen de pares clave-valor y son una alternativa para almacenar datos sin depender de la indexación. Esto nos permite recuperar valores basados en su significado y relación con otros tipos de datos.

0 Comments

Creative Commons License