Maps en Go: creación, lectura segura con el patrón ok y delete

Los maps en Go son tablas hash no ordenadas que asocian claves con valores. Son el tipo de colección clave-valor del lenguaje estándar y se usan ampliamente para contar, agrupar, cachear y representar datos estructurados cuando no se conoce el esquema en tiempo de compilación.

Crear un map

package main

import "fmt"

func main() {
    // con make
    edades := make(map[string]int)
    edades["Ana"] = 30
    edades["Luis"] = 25

    // literal con valores iniciales
    colores := map[string]string{
        "rojo":  "#FF0000",
        "verde": "#00FF00",
        "azul":  "#0000FF",
    }

    fmt.Println(edades["Ana"])    // 30
    fmt.Println(colores["verde"]) // #00FF00
}

Leer claves sin errores: el patrón ok

Leer una clave que no existe devuelve el zero value del tipo de valor, sin error ni pánico. Para saber si la clave existe, usa la forma de dos valores:

edad, ok := edades["Maria"]
if ok {
    fmt.Println("María tiene", edad, "años")
} else {
    fmt.Println("María no está en el map") // este caso
}

// También funciona en una sola línea:
if v, ok := colores["amarillo"]; ok {
    fmt.Println("amarillo:", v)
}

Añadir, actualizar y eliminar

// añadir o actualizar
edades["Carlos"] = 40
edades["Ana"] = 31  // sobreescribe el valor anterior

// eliminar
delete(edades, "Luis")

// delete sobre clave inexistente no hace nada ni produce error
delete(edades, "no_existe")

Iterar con range

El orden de iteración sobre un map es intencionalmente aleatorio en Go. No dependas del orden:

puntuaciones := map[string]int{
    "Ana":   95,
    "Luis":  87,
    "Marta": 92,
}

for nombre, puntos := range puntuaciones {
    fmt.Printf("%s: %dn", nombre, puntos)
}
// orden arbitrario en cada ejecución

Si necesitas orden, recoge las claves en un slice y ordénalo:

import "sort"

claves := make([]string, 0, len(puntuaciones))
for k := range puntuaciones {
    claves = append(claves, k)
}
sort.Strings(claves)

for _, k := range claves {
    fmt.Printf("%s: %dn", k, puntuaciones[k])
}

Map nil: el error que desconcierta a los principiantes

Un map declarado pero no inicializado es nil. Leer de un map nil es seguro (devuelve zero values), pero escribir en él produce un pánico:

var m map[string]int
fmt.Println(m["clave"]) // 0 — seguro leer
m["clave"] = 1          // panic: assignment to entry in nil map

Inicializa siempre con make o un literal antes de escribir.

Maps y goroutines: problema de concurrencia

Los maps en Go no son seguros para escrituras concurrentes. Si dos goroutines escriben en el mismo map simultáneamente, el programa puede entrar en pánico o producir corrupción de datos. El detector de carreras lo detecta:

go run -race main.go
# WARNING: DATA RACE
# Write at 0x00c0000b8000 by goroutine 7

Para uso concurrente, usa sync.RWMutex o el tipo sync.Map de la biblioteca estándar.

Contar frecuencias: patrón típico

texto := "programacion en go y mas go y go"
palabras := strings.Fields(texto)
frecuencia := make(map[string]int)

for _, p := range palabras {
    frecuencia[p]++ // zero value es 0, así que funciona desde el primer uso
}

fmt.Println(frecuencia["go"]) // 3

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP