Ma

Maps en Go

2 ejercicios

Acerca de Maps

En Go, map es un tipo de dato incorporado que representa una tabla hash. En otros lenguajes de programación, puede que conozcas el map como dict, como array asociativo o como almacén de clave/valor. Si no estás familiarizado con este concepto, puedes pensar en el map como en un diccionario, en el que cada palabra es la key y la definición es el element del map.

Antes de empezar, me gustaría remitirte a la especificación de Go para los map y al blog de Go sobre los map para profundizar más en map.

Sintácticamente, map tiene este aspecto:

map[KeyType]ElementType

KeyType debe ser cualquier tipo comparable, mientras que ElementType puede ser cualquier tipo válido en Go, lo que significa que puedes almacenar desde una variable primitiva hasta un slice.

Es importante recordar que map en Go no está ordenado; si intentas recorrer un map con un bucle e imprimir los elementos, puede que te sorprendas al ver que se imprimen en un orden aleatorio (pruébalo si quieres).

También es importante saber que cada clave es única, lo que significa que asignar la misma clave dos veces sobrescribirá el valor de esa clave.

map es un tipo por referencia, lo que significa que si lo pasas de un sitio a otro, Go no copiará todo el map. En su lugar, lo que hace Go es copiar el puntero del map, lo que hace que pasar un map a una función o a una variable sea barato. El valor de un map sin inicializar es nil.

Puedes definir un map de la siguiente manera (a esto también lo llamamos un nil map);

  var foo map[string]int

Para inicializar un map, puedes hacer lo siguiente:

  // With map literal
  foo := map[string]int{}

o

  // or with make function
  foo := make(map[string]int)

Un map nil es distinto de un map inicializado; escribir en un map nil provocará un error en tiempo de ejecución

panic: assignment to entry in nil map

Por lo tanto, es importante inicializar un map antes de usarlo

Estas son algunas operaciones que puedes hacer con un map

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

Si intentas obtener el valor de una clave que no existe en el map, se devolverá el valor cero del tipo del valor. Esto puede confundirte, sobre todo si el valor predeterminado de tu ElementType (por ejemplo, 0 para un int) es un valor válido. Para comprobar si una clave existe en tu map, puedes usar

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

Como hemos visto antes, detectar si un map está inicializado o no es fácil, basta con compararlo con nil, pero ¿qué pasa con otros maps? Bueno, como map no es comparable, no podemos usar el operador ==, pero el paquete reflect tiene algo que nos puede rescatar: se llama DeepEqual y podemos usarlo así

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

Pero espera, si map no es comparable, ¿por qué podemos compararlo con nil? Bueno, la especificación ha hecho una excepción para esto, consulta la especificación de tipos comparables

Por último, si intentas escribir en un map desde varias goroutines, se activará el detector de carreras; consulta la documentación de Go y el blog de Go. Como alternativa, puedes usar sync.Map, atomic o mutex para sortear este problema.

Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Maps

La práctica está bloqueada

Desbloquea 3 ejercicios más para practicar Maps