Go Maps: Key-Value Pairs and Iteration
Maps are Go’s built-in hash table implementation. They provide efficient key-value storage and retrieval.
Map Basics
Creating Maps
package main
import "fmt"
func main() {
// Map literal
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Charlie": 35,
}
fmt.Println(ages)
// Using make()
scores := make(map[string]float64)
scores["Alice"] = 95.5
scores["Bob"] = 87.3
fmt.Println(scores)
// Empty map
empty := make(map[string]string)
fmt.Println(empty)
}
Accessing Values
package main
import "fmt"
func main() {
person := map[string]string{
"name": "Alice",
"city": "New York",
}
// Get value
name := person["name"]
fmt.Println(name) // Alice
// Check if key exists
city, ok := person["city"]
if ok {
fmt.Println(city) // New York
}
// Non-existent key returns zero value
country := person["country"]
fmt.Println(country) // "" (empty string)
// Check with ok
country, ok = person["country"]
if !ok {
fmt.Println("Country not found")
}
}
Map Operations
Adding and Updating
package main
import "fmt"
func main() {
m := make(map[string]int)
// Add
m["Alice"] = 30
m["Bob"] = 25
// Update
m["Alice"] = 31
fmt.Println(m)
}
Deleting
package main
import "fmt"
func main() {
m := map[string]int{
"Alice": 30,
"Bob": 25,
"Charlie": 35,
}
// Delete
delete(m, "Bob")
fmt.Println(m) // map[Alice:30 Charlie:35]
// Delete non-existent key (safe)
delete(m, "David")
fmt.Println(m)
}
Map Length
package main
import "fmt"
func main() {
m := map[string]int{
"Alice": 30,
"Bob": 25,
}
fmt.Println(len(m)) // 2
delete(m, "Alice")
fmt.Println(len(m)) // 1
}
Map Iteration
Range Over Maps
package main
import "fmt"
func main() {
person := map[string]string{
"name": "Alice",
"age": "30",
"city": "New York",
}
// Iterate with key and value
for key, value := range person {
fmt.Printf("%s: %s\n", key, value)
}
// Iterate with key only
for key := range person {
fmt.Println(key)
}
// Iterate with value only
for _, value := range person {
fmt.Println(value)
}
}
Iteration Order
package main
import "fmt"
func main() {
m := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
// Maps don't guarantee order
for key := range m {
fmt.Println(key)
}
// Output order is random each time
}
Map Patterns
Counting Occurrences
package main
import "fmt"
func countWords(words []string) map[string]int {
counts := make(map[string]int)
for _, word := range words {
counts[word]++
}
return counts
}
func main() {
words := []string{"apple", "banana", "apple", "cherry", "banana", "apple"}
counts := countWords(words)
fmt.Println(counts)
// Output: map[apple:3 banana:2 cherry:1]
}
Grouping Data
package main
import "fmt"
type Person struct {
Name string
City string
}
func groupByCity(people []Person) map[string][]Person {
groups := make(map[string][]Person)
for _, p := range people {
groups[p.City] = append(groups[p.City], p)
}
return groups
}
func main() {
people := []Person{
{"Alice", "New York"},
{"Bob", "Los Angeles"},
{"Charlie", "New York"},
}
grouped := groupByCity(people)
for city, residents := range grouped {
fmt.Printf("%s: %v\n", city, residents)
}
}
Caching Results
package main
import "fmt"
func fibonacci(n int, cache map[int]int) int {
if n <= 1 {
return n
}
if val, ok := cache[n]; ok {
return val
}
result := fibonacci(n-1, cache) + fibonacci(n-2, cache)
cache[n] = result
return result
}
func main() {
cache := make(map[int]int)
fmt.Println(fibonacci(10, cache))
}
Map of Maps
Nested Maps
package main
import "fmt"
func main() {
// Map of maps
data := map[string]map[string]int{
"Alice": {
"math": 95,
"english": 87,
},
"Bob": {
"math": 88,
"english": 92,
},
}
// Access nested value
fmt.Println(data["Alice"]["math"]) // 95
// Add nested value
data["Charlie"] = make(map[string]int)
data["Charlie"]["math"] = 91
fmt.Println(data)
}
Best Practices
โ Good: Check Key Existence
// DO: Check if key exists before accessing
if value, ok := m[key]; ok {
fmt.Println(value)
} else {
fmt.Println("Key not found")
}
โ Bad: Assume Key Exists
// DON'T: Assume key exists
value := m[key] // Returns zero value if not found
โ Good: Use Appropriate Key Types
// DO: Use appropriate types for keys
m := make(map[string]int) // String keys
m2 := make(map[int]string) // Int keys
m3 := make(map[struct{}]bool) // Struct keys
โ Good: Initialize Before Use
// DO: Initialize map before adding values
m := make(map[string][]int)
m["key"] = append(m["key"], 1) // Safe
โ Bad: Nil Map
// DON'T: Use nil map
var m map[string]int
m["key"] = 1 // Panic!
Summary
Go maps provide:
- Efficient key-value storage with hash tables
- Fast lookups in O(1) average time
- Flexible key types (any comparable type)
- Safe access with comma-ok idiom
- Easy iteration with range
These features make maps essential for Go programming.
Comments