Home Go Tutorial on Maps in Go

Tutorial on Maps in Go

In Go, a map is a built-in data structure that associates keys with values.

Maps are often used to look up values based on a unique key, making them ideal for scenarios where fast data retrieval is essential.

Go’s maps are implemented as hash tables, providing average constant-time complexity for operations like inserting, retrieving, and deleting elements.

In this tutorial, we’ll cover:

1. Declaring and Initializing Maps

Maps in Go are declared using the map keyword, followed by the key type and value type. Maps can be initialized using make() or by using a map literal.

Using make() to Initialize a Map

package main

import "fmt"

func main() {
    // Declare and initialize a map
    ages := make(map[string]int) // Key: string, Value: int

    ages["Alice"] = 25
    ages["Bob"] = 30

    fmt.Println("Ages map:", ages)
}

Output:

Ages map: map[Alice:25 Bob:30]

In this example:

  • make(map[string]int) creates an empty map where keys are of type string and values are of type int.

Using a Map Literal

You can also initialize a map with predefined values using a map literal.

package main

import "fmt"

func main() {
    // Initialize a map with values
    scores := map[string]int{
        "Alice": 90,
        "Bob":   85,
        "Carol": 92,
    }

    fmt.Println("Scores map:", scores)
}

Output:

Scores map: map[Alice:90 Bob:85 Carol:92]

In this example:

  • map[string]int{ … } creates and initializes a map with key-value pairs in a single line.

2. Accessing and Modifying Map Elements

You can access and modify map elements using the key. If a key exists in the map, it returns the corresponding value. If the key doesn’t exist, it returns the zero value for the map's value type.

package main

import "fmt"

func main() {
    prices := map[string]float64{
        "apple":  0.99,
        "banana": 1.49,
    }

    // Accessing elements
    fmt.Println("Price of apple:", prices["apple"])

    // Modifying an element
    prices["apple"] = 1.09
    fmt.Println("Updated price of apple:", prices["apple"])

    // Accessing a non-existent key (returns zero value for float64)
    fmt.Println("Price of orange:", prices["orange"])
}

Output:

Price of apple: 0.99
Updated price of apple: 1.09
Price of orange: 0

In this example:

  • Accessing prices[“apple”] retrieves the value associated with the key “apple”.
  • Modifying prices[“apple”] changes its value.
  • Accessing a non-existent key like “orange” returns 0.0 (the zero value for float64).

3. Adding and Deleting Elements

You can add elements to a map by assigning a value to a new key and delete elements using the delete() function.

package main

import "fmt"

func main() {
    inventory := map[string]int{
        "pencil":  10,
        "pen":     5,
        "notebook": 15,
    }

    // Adding a new element
    inventory["eraser"] = 7
    fmt.Println("Inventory after adding eraser:", inventory)

    // Deleting an element
    delete(inventory, "pen")
    fmt.Println("Inventory after deleting pen:", inventory)
}

Output:

Inventory after adding eraser: map[eraser:7 notebook:15 pen:5 pencil:10]
Inventory after deleting pen: map[eraser:7 notebook:15 pencil:10]

In this example:

  • inventory[“eraser”] = 7 adds a new key-value pair to the map.
  • delete(inventory, “pen”) removes the “pen” entry from the map.

4. Checking if a Key Exists in a Map

When accessing a key in a map, you can use a second return value to check if the key exists.

package main

import "fmt"

func main() {
    items := map[string]int{
        "apple":  10,
        "banana": 5,
    }

    // Checking if a key exists
    if value, exists := items["apple"]; exists {
        fmt.Println("Apple exists with quantity:", value)
    } else {
        fmt.Println("Apple does not exist.")
    }

    if value, exists := items["orange"]; exists {
        fmt.Println("Orange exists with quantity:", value)
    } else {
        fmt.Println("Orange does not exist.")
    }
}

Output:

Apple exists with quantity: 10
Orange does not exist.

In this example:

  • value, exists := items[“apple”] retrieves the value and a boolean indicating if the key exists.
  • The exists variable allows you to check if a key is present in the map.

5. Iterating Over Maps

You can iterate over maps using a for loop with range, which provides both the key and value.

package main

import "fmt"

func main() {
    prices := map[string]float64{
        "milk":   2.5,
        "bread":  1.5,
        "cheese": 3.75,
    }

    for item, price := range prices {
        fmt.Printf("Item: %s, Price: %.2f\n", item, price)
    }
}

Output:

Item: milk, Price: 2.50
Item: bread, Price: 1.50
Item: cheese, Price: 3.75

In this example:

  • for item, price := range prices iterates over each key-value pair in the prices map, printing both the item and price.

6. Nested Maps

Maps can contain other maps as values, allowing you to create complex data structures like nested dictionaries.

package main

import "fmt"

func main() {
    employees := map[string]map[string]int{
        "Engineering": {
            "Alice": 30,
            "Bob":   25,
        },
        "Marketing": {
            "Carol": 28,
            "Dave":  35,
        },
    }

    // Accessing nested map elements
    fmt.Println("Alice's age in Engineering:", employees["Engineering"]["Alice"])

    // Adding a new department and employee
    employees["Sales"] = map[string]int{"Eve": 27}
    fmt.Println("Employees:", employees)
}

Output:

Alice's age in Engineering: 30
Employees: map[Engineering:map[Alice:30 Bob:25] Marketing:map[Carol:28 Dave:35] Sales:map[Eve:27]]

In this example:

  • employees is a nested map where each department name maps to another map of employee names and their ages.

7. Best Practices for Using Maps in Go

a) Choose Appropriate Key Types

Keys in Go maps must be comparable (i.e., types that support == and !=), such as string, int, or custom types with comparable fields. Avoid using slices or structs with non-comparable fields as map keys.

inventory := map[string]int{
    "pencil":  10,
    "notebook": 15,
}

b) Check for Key Existence

Use the second return value when accessing a map to verify if a key exists, especially if your code relies on the presence of specific keys.

if value, exists := myMap["key"]; exists {
    // key exists, use value
}

c) Use make() for Large Maps

Use make() to create large maps with an initial capacity to avoid performance issues due to frequent resizing.

largeMap := make(map[int]string, 1000) // Initialize with capacity of 1000

d) Avoid Unnecessary Map Deletions

Removing keys from a map is generally inexpensive, but if you are reusing the map, keep deletions minimal to improve efficiency.

e) Use Nested Maps with Caution

Nested maps increase complexity and may be harder to read. For complex structures, consider using structs with embedded fields instead.

Summary

In this tutorial, we covered the basics of maps in Go:

  1. Declaring and Initializing Maps: Using make() or map literals to create maps.
  2. Accessing and Modifying Map Elements: Adding, modifying, and retrieving elements by key.
  3. Adding and Deleting Elements: Using map indexing and delete() to manage map contents.
  4. Checking if a Key Exists in a Map: Using the second return value for key existence checks.
  5. Iterating Over Maps: Using for-range to loop through map elements.
  6. Nested Maps: Creating complex data structures with maps within maps.

Best Practices for Using Maps in Go: Writing efficient, clean, and performant code with maps.

Maps are a powerful and flexible data structure in Go, ideal for fast lookups and efficient data management. By mastering maps, you’ll be able to store and retrieve data in your Go programs efficiently and effectively.

 

You may also like