Introducción
A diferencia de otros lenguajes, en Go, los diccionarios (maps) son colecciones desordenadas. En esta práctica de laboratorio, aprenderemos sobre la ordenación de diccionarios y cómo utilizarlos de manera más flexible.
Conceptos Clave:
- Ordenación de diccionarios
- Intercambio de claves y valores en un diccionario
- Slices de diccionarios
- Diccionarios con slices como valores
- Características de los diccionarios como tipos de referencia
Ordenación de Diccionarios
Crea un archivo map.go en el directorio ~/project:
touch ~/project/map.go
Escribe el siguiente código en el archivo map.go:
package main
import (
"fmt"
)
func main() {
// Declare and initialize a map with string keys and integer values
// The map stores student names and their scores
m := map[string]int{
"Alice": 99, // Each key-value pair represents a student and their score
"Bob": 38,
"Charlie": 84,
}
// Iterate through the map using a for-range loop
// 'key' represents student names, 'value' represents scores
for key, value := range m {
fmt.Println(key, value)
}
fmt.Println("\nInsert Data")
// Demonstrate how to add a new key-value pair to the map
// The syntax is: map[key] = value
m["David"] = 25
// Iterate again to show the updated map contents
// Note that the order may be different in each execution
for key, value := range m {
fmt.Println(key, value)
}
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa podría verse así:
Charlie 84
Bob 38
Alice 99
Insert Data
David 25
Charlie 84
Bob 38
Alice 99
La salida puede variar ya que el orden de los datos insertados no es fijo. Esta es una característica fundamental de los maps en Go: no garantizan ningún orden particular de los elementos cuando se itera a través de ellos.
Puedes intentar ejecutar el programa varias veces y observar que el orden de los datos insertados puede cambiar. Esto ilustra que no puedes confiar en el orden de los elementos en un map.
Sin embargo, a veces necesitamos ordenar el diccionario después de insertar los datos. ¿Cómo podemos lograrlo?
Dado que un map en sí mismo no se puede ordenar, podemos convertir el map en un slice y luego ordenar dicho slice.
Ordenar por Clave
Primero, aprendamos cómo ordenar el diccionario por su clave.
Estos son los pasos:
- Convertir las claves del diccionario en un slice. Un slice es una estructura ordenada y dinámica que podemos clasificar.
- Ordenar el slice utilizando el paquete nativo
sortde Go. - Recuperar los valores correspondientes utilizando el método de búsqueda del diccionario. Como conocemos el orden de las claves en nuestro slice, podemos buscar los valores en el map y aparecerán en ese orden de claves ordenadas.
Escribe el siguiente código en el archivo map.go:
package main
import (
"fmt"
"sort"
)
func main() {
// Initialize the dictionary
m1 := map[int]string{
3: "Bob",
1: "Alice",
2: "Charlie",
}
keys := make([]int, 0, len(m1)) // Initialize the slice with capacity. This is a performance optimization - the slice will allocate memory equal to the size of the map initially, avoiding re-allocations.
for key := range m1 {
// Convert the key to a slice
keys = append(keys, key)
}
// Sort the key slice using the sort package. The `sort.Ints()` function specifically sorts a slice of integers.
sort.Ints(keys)
for _, key := range keys {
// The output is in order now
fmt.Println(key, m1[key])
}
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
1 Alice
2 Charlie
3 Bob
Mediante este enfoque, hemos logrado ordenar el diccionario basándonos en la clave. Las claves se extraen a un slice, se ordenan y luego se usan para buscar e imprimir los valores correspondientes del map.
Intercambio de Claves y Valores en un Diccionario
Antes de explicar cómo ordenar por valor, aprendamos a intercambiar las claves y los valores en un diccionario.
Intercambiar claves y valores significa permutar las posiciones de ambos en un diccionario, como se muestra en la siguiente imagen:

El código de implementación es sencillo. Escribe el siguiente código en el archivo map.go:
package main
import "fmt"
func main() {
m := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
m2 := map[int]string{}
for key, value := range m {
m2[value] = key
}
fmt.Println(m2)
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
map[38:Bob 84:Charlie 99:Alice]
La esencia de este código es extraer las claves y valores del diccionario original y luego reinsertarlos en un nuevo diccionario pero con los roles intercambiados. Es simple y directo. Ten en cuenta que, debido a que el map no tiene orden, el orden de salida del map intercambiado puede variar.
Ordenar por Valor
Combinando la lógica de ordenar el diccionario por clave y el intercambio de claves y valores, podemos ordenar el diccionario por su valor.
Así es como funciona: intercambiamos las claves y los valores, y luego tratamos las claves intercambiadas (que eran los valores originales) como la base para nuestra ordenación. Después, usamos esas "claves" ordenadas (valores originales) para buscar las claves originales correspondientes en el map intercambiado.
Escribe el siguiente código en el archivo map.go:
package main
import (
"fmt"
"sort"
)
func main() {
// Initialize the dictionary
m1 := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
// Initialize the reversed dictionary
m2 := map[int]string{}
for key, value := range m1 {
// Generate the reversed dictionary m2 by swapping the key-value pairs
m2[value] = key
}
values := make([]int, 0) // Initialize the sorting slice
for _, value := range m1 {
// Convert the values of the original dictionary to a slice
values = append(values, value)
}
// Sort the value slice using the sort package
sort.Ints(values)
for _, value := range values {
// The output is in order now
fmt.Println(m2[value], value)
}
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
Bob 38
Charlie 84
Alice 99
Ahora hemos ordenado el diccionario basándonos en sus valores. Convertimos los valores a un slice, ordenamos el slice y luego usamos los valores ordenados para recuperar e imprimir las claves originales correspondientes desde el map intercambiado.
Ordenación mediante sort.Slice
Si la versión de Go es posterior a la 1.7, podemos usar la función sort.Slice para ordenar rápidamente un map por clave o valor. sort.Slice permite especificar una función de comparación personalizada.
Aquí tienes un ejemplo:
package main
import (
"fmt"
"sort"
)
func main() {
m1 := map[int]int{
21: 99,
12: 98,
35: 17,
24: 36,
}
type kv struct {
Key int
Value int
}
var s1 []kv
for k, v := range m1 {
s1 = append(s1, kv{k, v})
}
sort.Slice(s1, func(i, j int) bool {
return s1[i].Key < s1[j].Key
})
fmt.Println("Sorted in ascending order by key:")
for _, pair := range s1 {
fmt.Printf("%d, %d\n", pair.Key, pair.Value)
}
}
Ejecuta el programa:
go run ~/project/map.go
La salida es la siguiente:
Sorted in ascending order by key:
12, 98
21, 99
24, 36
35, 17
En este programa, utilizamos una estructura kv para almacenar los pares clave-valor del map. Luego, ordenamos el slice de estructuras usando la función sort.Slice() y una función de comparación anónima. Esta función de comparación (func(i, j int) bool) determina el orden de clasificación basándose en el campo Key de nuestra estructura.
También podemos usar sort.Slice para ordenar el map en orden descendente por clave o en orden ascendente por valor modificando esta función de comparación. Esto proporciona una gran flexibilidad en la forma en que queremos organizar los datos de nuestro map.
Pequeño Desafío
Crea un archivo map2.go y modifica el código de la sección anterior para ordenar el map en orden descendente basándote en el valor.
Salida Esperada:
Ejecuta el programa:
go run ~/project/map2.go
Sorted in descending order by value:
21, 99
12, 98
24, 36
35, 17
Requisitos:
- El archivo
map2.godebe ubicarse en el directorio~/project. - Debes utilizar la función
sort.Slice. Necesitarás modificar la función de comparación utilizada ensort.Slicedel ejemplo anterior.
Slices de Diccionarios
Al igual que podemos usar arrays o slices para almacenar datos relacionados, también podemos usar slices donde los elementos son diccionarios. Esto nos permite manejar colecciones de datos tipo map, lo cual es muy útil para trabajar con información estructurada.
Escribe el siguiente código en el archivo map.go:
package main
import "fmt"
func main() {
// Declare a slice of maps and initialize it using make
var mapSlice = make([]map[string]string, 3) // Creates a slice with a capacity of 3, where each element can be a `map[string]string`.
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("Initialization")
// Assign values to the first element of the slice
mapSlice[0] = make(map[string]string, 10) // Creates a map at the first index.
mapSlice[0]["name"] = "labex"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "Paris"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
index:0 value:map[]
index:1 value:map[]
index:2 value:map[]
Initialization
index:0 value:map[address:Paris name:labex password:123456]
index:1 value:map[]
index:2 value:map[]
El código demuestra la inicialización de un slice de maps. Inicialmente, cada elemento del slice es un map vacío. Luego, creamos y asignamos un map al primer elemento, poblándolo con datos. Esto muestra cómo gestionar una lista de diccionarios.
Diccionarios con Slices como Valores
También podemos usar diccionarios con slices como valores para almacenar más datos en un solo diccionario. Esto permite asociar múltiples valores a una única clave en un map, creando efectivamente una relación de "uno a muchos".
Escribe el siguiente código en el archivo map.go:
package main
import "fmt"
func main() {
var sliceMap = make(map[string][]string, 3) // Declares a map where keys are strings and values are slices of strings. The 3 is a capacity hint.
key := "labex"
value, ok := sliceMap[key] // Checks if the key exists
if !ok {
value = make([]string, 0, 2) // If it doesn't exist, initialize a new slice with capacity.
}
value = append(value, "Paris", "Shanghai") // Appends values to the slice.
sliceMap[key] = value // Sets the slice as the value for our key in the map.
fmt.Println(sliceMap)
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
map[labex:[Paris Shanghai]]
El código primero declara un tipo de map donde los valores son slices. Verifica si la clave existe antes de agregar nuevos elementos al slice asociado, demostrando un patrón común para manejar maps de slices.
Características de los Diccionarios como Tipos de Referencia
Los arrays son tipos de valor, por lo que asignarlos y pasarlos a funciones crea una copia. Los cambios en la copia no afectan al array original. Los maps, sin embargo, son tipos de referencia. Esto significa que asignar y pasar un map a una función no crea una copia completa. En su lugar, el map se pasa por referencia.
Esto es importante, ya que los cambios realizados dentro de la función afectarán a los datos del map original.
Escribe el siguiente código en el archivo map.go:
package main
import "fmt"
func modifyMap(x map[string]int) {
x["Bob"] = 100 // Modifies the map that was passed as an argument.
}
func main() {
a := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
// Because the map is passed by reference, the modification in modifyMap changes the original dictionary
modifyMap(a)
fmt.Println(a) // map[Alice:99 Bob:100 Charlie:84]
}
Ejecuta el programa:
go run ~/project/map.go
La salida del programa es la siguiente:
map[Alice:99 Bob:100 Charlie:84]
En este ejemplo, demostramos la característica de transferencia por referencia de un diccionario. La función modifyMap cambia el map original porque a es una referencia a los mismos datos subyacentes del map. Es crucial entender este comportamiento al pasar maps a funciones.
Resumen
En esta práctica de laboratorio, hemos aprendido sobre el uso avanzado de los maps en Go, incluyendo:
- Ordenación de maps por clave o valor, lo que implica convertir el map en un slice y luego ordenar dicho slice.
- Intercambio de claves y valores en un map, lo que crea un nuevo map con los roles invertidos.
- Uso de slices de maps, lo que permite crear colecciones de datos de maps relacionados.
- Uso de maps con slices como valores, lo que permite asociar múltiples valores con una sola clave.
- Características de los maps como tipos de referencia, donde los cambios en un map pasado se reflejan en el map original.
Comprender estos conceptos te permitirá utilizar los maps de Go de manera más efectiva en aplicaciones del mundo real.



