Exercício – Usar mapas

Concluído

Um mapa do Go é basicamente uma tabela de hash, que é uma coleção de pares chave e valor. Todas as chaves de um mapa precisam ser do mesmo tipo e ter os mesmos valores. No entanto, você pode usar tipos diferentes para chaves e valores. Por exemplo, as chaves podem ser números, e os valores podem ser cadeias de caracteres. Para acessar um item específico em um mapa, referencie a chave dele.

Declarar e inicializar um mapa

Para declarar um mapa, você precisará usar a palavra-chave map. Em seguida, defina a chave e o tipo de valor, desta forma: map[T]T. Por exemplo, caso deseje criar um mapa que contenha a idade dos alunos, use o seguinte código:

package main

import "fmt"

func main() {
    studentsAge := map[string]int{
        "john": 32,
        "bob":  31,
    }
    fmt.Println(studentsAge)
}

Ao executar o código anterior, você verá a seguinte saída:

map[bob:31 john:32]

Se você não quiser inicializar um mapa com itens, use a função interna make() para criar o mapa na seção anterior. Use o seguinte código para criar um mapa vazio:

studentsAge := make(map[string]int)

Os mapas são dinâmicos. Você pode adicionar, acessar ou remover itens depois de criá-los. Vamos explorar essas ações.

Adicionar itens

Para adicionar itens, você não precisa usar uma função interna como faz com as fatias. Os mapas são mais simples. Você só precisa definir uma chave e um valor. Se o par não existir, o item será adicionado ao mapa.

Vamos reescrever o código que usamos anteriormente para criar um mapa usando a função make. Em seguida, adicionaremos itens ao mapa. Você poderia usar o seguinte código:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

Ao executar o código, você obterá a mesma saída que antes:

map[bob:31 john:32]

Observe que adicionamos itens a um mapa que foi inicializado. Mas se você tentar fazer o mesmo com um mapa nil, receberá um erro. Por exemplo, o seguinte código não funcionará:

package main

import "fmt"

func main() {
    var studentsAge map[string]int
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

Ao executar o código anterior, você receberá o seguinte erro:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/johndoe/go/src/helloworld/main.go:7 +0x4f
exit status 2

Para evitar problemas ao adicionar itens a um mapa, crie um mapa vazio (não um mapa nil) usando a função make como mostramos no snippet de código anterior. Essa regra só se aplica quando você adiciona itens. Se você executar pesquisas, exclusões ou operações de loop em um mapa nil, isso não fará com que o Go entre em pane. Confirmaremos esse comportamento daqui a pouco.

Acessar itens

Para acessar itens em um mapa, use a notação de subscrito habitual m[key], como você faz com matrizes ou fatias. Veja um exemplo simples de como acessar um item:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Bob's age is", studentsAge["bob"])
}

Quando você usa a notação de subscrito em um mapa, sempre recebe uma resposta, mesmo que a chave não esteja presente em um mapa. Não ocorre pane no Go quando você acessa um item que não existe. Em vez disso, o valor padrão é retornado. Confirme esse comportamento usando o seguinte código:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println("Christy's age is", studentsAge["christy"])
}

Ao executar o código anterior, você verá a seguinte saída:

Christy's age is 0

Em muitos casos, é justo que o Go não retorne um erro quando você acessa um item que não existe em um mapa. Mas há ocasiões em que você precisará saber se um item existe ou não. No Go, a notação de subscrito de um mapa pode produzir dois valores. O primeiro é o valor de um item. O segundo é um sinalizador booliano que indica se a chave existe ou não.

Para corrigir o problema com o último snippet de código, use o seguinte código:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    age, exist := studentsAge["christy"]
    if exist {
        fmt.Println("Christy's age is", age)
    } else {
        fmt.Println("Christy's age couldn't be found")
    }
}

Ao executar o código anterior, você verá a seguinte saída:

Christy's age couldn't be found

Use o segundo snippet de código para verificar se uma chave existe em um mapa antes de acessá-la.

Remover itens

Para remover um item de um mapa, use a função interna delete(). Veja um exemplo de como remover itens de um mapa:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "john")
    fmt.Println(studentsAge)
}

Ao executar o código, você obterá a seguinte saída:

map[bob:31]

Como dissemos antes, se você tentar excluir um item que não existe, isso não será indicado como uma pane no Go. Veja um exemplo desse comportamento:

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    delete(studentsAge, "christy")
    fmt.Println(studentsAge)
}

Ao executar o código, você não receberá um erro e verá a seguinte saída:

map[bob:31 john:32]

Fazer um loop em um mapa

Por fim, vejamos como fazer um loop em um mapa para acessar todos os itens de modo programático. Para fazer isso, use o loop baseado em intervalo, como neste exemplo:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}

Ao executar o código anterior, você verá a seguinte saída:

john    32
bob     31

Observe como você pode armazenar as informações de chave e valor em variáveis diferentes. Nesse caso, estamos armazenando a chave na variável name e o valor na variável age. Portanto, range gera primeiro a chave de um item e, depois, o valor desse item. Você pode ignorar uma delas usando a variável _, como neste exemplo:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for _, age := range studentsAge {
        fmt.Printf("Ages %d\n", age)
    }
}

Embora, nesse caso, não faça sentido imprimir as idades dessa forma, haverá casos em que você não precisará saber a chave de um item. Como alternativa, você pode usar apenas a chave do item, como neste exemplo:

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31

    for name := range studentsAge {
        fmt.Printf("Names %s\n", name)
    }
}