Introdução
Diferente de outras linguagens, em Go, os dicionários (maps) são coleções não ordenadas. Neste laboratório, aprenderemos sobre a ordenação de dicionários e como utilizá-los de forma mais flexível.
Conceitos Chave:
- Ordenação de dicionários
- Inversão de chaves e valores em um dicionário
- Slices de dicionários
- Dicionários com slices como valores
- Características de tipo de referência dos dicionários
Ordenação de Dicionários
Crie um arquivo map.go no diretório ~/project:
touch ~/project/map.go
Escreva o seguinte código no arquivo 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)
}
}
Execute o programa:
go run ~/project/map.go
A saída do programa pode ser parecida com esta:
Charlie 84
Bob 38
Alice 99
Insert Data
David 25
Charlie 84
Bob 38
Alice 99
A saída pode variar, pois a ordem dos dados inseridos não é fixa. Esta é uma característica fundamental dos maps em Go — eles não garantem nenhuma ordem específica dos elementos quando você os percorre.
Você pode tentar executar o programa várias vezes e observar que a ordem dos dados inseridos pode mudar. Isso ilustra que você não pode confiar na ordem dos elementos em um map.
Mas às vezes precisamos ordenar o dicionário após a inserção dos dados. Como podemos conseguir isso?
Como um map por si só não pode ser ordenado, podemos converter o map em um slice e então ordenar o slice.
Ordenar por Chave
Primeiro, vamos aprender como ordenar o dicionário pela chave.
Aqui estão as etapas:
- Converter as chaves do dicionário em um slice. Um slice é um array dinâmico e ordenado que podemos classificar.
- Ordenar o slice usando o pacote nativo
sortdo Go. - Recuperar os valores correspondentes usando o método de busca do dicionário. Como conhecemos a ordem das chaves em nosso slice, podemos buscar os valores no map e eles aparecerão na ordem das chaves ordenadas.
Escreva o seguinte código no arquivo 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])
}
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
1 Alice
2 Charlie
3 Bob
Através desta abordagem, conseguimos ordenar o dicionário com base na chave. As chaves são extraídas para um slice, ordenadas e então usadas para buscar e imprimir os valores correspondentes do map.
Inversão de Chaves e Valores em um Dicionário
Antes de explicarmos como ordenar por valor, vamos aprender como inverter as chaves e os valores em um dicionário.
Inverter chaves e valores significa trocar as posições entre eles no dicionário, como mostrado na figura abaixo:

O código de implementação é simples. Escreva o seguinte código no arquivo 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)
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
map[38:Bob 84:Charlie 99:Alice]
A essência deste código é extrair as chaves e valores do dicionário original e reinseri-los no dicionário, mas com os papéis trocados. É simples e direto. Note que, como o map não é ordenado, a ordem de saída do map invertido pode variar.
Ordenar por Valor
Combinando a lógica de ordenação do dicionário por chave e a inversão de chaves e valores, podemos ordenar o dicionário por valor.
Funciona assim: invertemos as chaves e os valores e, em seguida, tratamos as chaves invertidas (valores originais) como a base para nossa ordenação. Depois, usamos as "chaves" ordenadas (valores originais) para buscar as chaves originais correspondentes no map invertido.
Escreva o seguinte código no arquivo 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)
}
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
Bob 38
Charlie 84
Alice 99
Agora ordenamos o dicionário com base em seus valores. Convertemos os valores para um slice, ordenamos o slice e usamos os valores ordenados para recuperar e imprimir as chaves originais correspondentes do map invertido.
Ordenação com sort.Slice
Se a versão do Go for superior à 1.7, podemos usar a função sort.Slice para ordenar rapidamente um map por chave ou valor. O sort.Slice permite que você especifique uma função de comparação personalizada.
Aqui está um exemplo:
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)
}
}
Execute o programa:
go run ~/project/map.go
A saída é a seguinte:
Sorted in ascending order by key:
12, 98
21, 99
24, 36
35, 17
Neste programa, usamos uma struct kv para armazenar os pares chave-valor do map. Em seguida, ordenamos o slice de structs usando a função sort.Slice() e uma função de comparação anônima. Esta função de comparação (func(i, j int) bool) determina a ordem de classificação com base no campo Key da nossa struct.
Também podemos usar o sort.Slice para ordenar o map em ordem decrescente por chave ou em ordem crescente por valor, modificando esta função de comparação. Isso proporciona grande flexibilidade na forma como queremos ordenar os dados do nosso map.
Pequeno Teste
Crie um arquivo map2.go e modifique o código da seção anterior para ordenar o map em ordem decrescente com base no valor.
Saída Esperada:
Execute o programa:
go run ~/project/map2.go
Sorted in descending order by value:
21, 99
12, 98
24, 36
35, 17
Requisitos:
- O arquivo
map2.godeve ser colocado no diretório~/project. - Você deve usar a função
sort.Slice. Você precisará modificar a função de comparação usada nosort.Slicedo exemplo anterior.
Slices de Dicionários
Assim como podemos usar arrays ou slices para armazenar dados relacionados, também podemos usar slices onde os elementos são dicionários. Isso nos permite manter coleções de dados de map, o que pode ser útil para trabalhar com informações estruturadas.
Escreva o seguinte código no arquivo 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)
}
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
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[]
O código demonstra a inicialização de um slice de maps. Inicialmente, cada elemento no slice é um map vazio. Em seguida, criamos e atribuímos um map ao primeiro elemento, preenchendo-o com dados. Isso mostra como gerenciar uma lista de maps.
Dicionários com Slices como Valores
Também podemos usar dicionários com slices como valores para armazenar mais dados em um dicionário. Isso nos permite associar múltiplos valores a uma única chave em um map, criando efetivamente um relacionamento "um para muitos".
Escreva o seguinte código no arquivo 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)
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
map[labex:[Paris Shanghai]]
O código primeiro declara um tipo de map onde os valores são slices. Ele verifica se a chave existe antes de adicionar novos itens ao slice associado, demonstrando um padrão comum para lidar com maps de slices.
Características de Tipo de Referência dos Dicionários
Arrays são tipos de valor, portanto, atribuí-los e passá-los para funções cria uma cópia. Alterações na cópia não afetam o array original. Os maps, no entanto, são tipos de referência. Isso significa que atribuir e passar um map para uma função não cria uma cópia completa. Em vez disso, o map é passado por referência.
Isso é importante, pois as alterações feitas dentro da função irão afetar os dados do map original.
Escreva o seguinte código no arquivo 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]
}
Execute o programa:
go run ~/project/map.go
A saída do programa é a seguinte:
map[Alice:99 Bob:100 Charlie:84]
No exemplo, demonstramos a característica de transferência por referência de um dicionário. A função modifyMap altera o map original porque a é uma referência aos mesmos dados subjacentes do map. É crucial entender esse comportamento ao passar maps para funções.
Resumo
Neste laboratório, aprendemos sobre o uso avançado de maps em Go, incluindo:
- Ordenação de maps por chave ou valor, o que envolve converter o map em um slice e então ordenar o slice.
- Inversão de chaves e valores em um map, o que cria um novo map com papéis invertidos.
- Uso de slices de maps, o que permite criar coleções de dados de maps relacionados.
- Uso de maps com slices como valores, o que permite associar múltiplos valores a uma única chave.
- Características de tipo de referência dos maps, onde alterações em um map passado refletem no map original.
Compreender esses conceitos permitirá que você utilize os maps do Go de forma mais eficaz em aplicações do mundo real.



