Slices & Maps
Introduction
Go has two primary collection types: slices and maps. A slice is an ordered, dynamically-sized sequence of elements of a single type — the closest equivalent to a Python list. A map is an unordered collection of key-value pairs — the closest equivalent to a Python dictionary.
The key difference from Python is that Go collections are typed. A []string can only hold strings. A map[string]int can only hold string keys and integer values. Trying to put the wrong type in either will cause a compile error.
Maps in Go must be initialised before use with make(map[K]V) or a map literal. A declared but uninitialised map has the value nil, and writing to a nil map causes a runtime panic. This is a common source of bugs for developers coming from Python, where {} is always ready to use.
Concepts
Slices
// Slice literal
names := []string{"Alice", "Bob", "Charlie"}
// Append (returns a new slice — Go slices are immutable in length)
names = append(names, "Dave")
// Access and slice
fmt.Println(names[0]) // Alice
fmt.Println(names[1:3]) // [Bob Charlie]
fmt.Println(len(names)) // 4
// Create with make (length 0, capacity 5)
scores := make([]int, 0, 5)
scores = append(scores, 10, 20, 30)
Maps
// Map literal
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
// Initialise with make
counts := make(map[string]int)
// Add / update
ages["Charlie"] = 35
// Read (with existence check)
age, ok := ages["Alice"]
if ok {
fmt.Println("Alice is", age)
}
// Delete
delete(ages, "Bob")
// Iterate
for name, age := range ages {
fmt.Printf("%s: %d\n", name, age)
}
Examples
package main
import (
"fmt"
"sort"
)
func main() {
// Slice operations
numbers := []int{5, 2, 8, 1, 9, 3}
sort.Ints(numbers)
fmt.Println("Sorted:", numbers)
fmt.Println("First:", numbers[0], "Last:", numbers[len(numbers)-1])
// Word count with a map
words := []string{"go", "is", "fast", "go", "is", "compiled", "go"}
counts := make(map[string]int)
for _, word := range words {
counts[word]++
}
for word, count := range counts {
fmt.Printf("%s: %d\n", word, count)
}
}
Challenges
1. Create and iterate a slice
- Create a slice of five city names.
- Print each one using a range loop with its index.
💡 Show Answer
package main
import "fmt"
func main() {
cities := []string{"London", "Tokyo", "New York", "Paris", "Sydney"}
for i, city := range cities {
fmt.Printf("%d: %s\n", i+1, city)
}
}
2. Append to a slice
- Start with an empty
[]intslice. - Append the squares of numbers 1 through 10.
- Print the resulting slice.
💡 Show Answer
package main
import "fmt"
func main() {
squares := []int{}
for i := 1; i <= 10; i++ {
squares = append(squares, i*i)
}
fmt.Println(squares) // [1 4 9 16 25 36 49 64 81 100]
}
3. Slice of a slice
- Create a slice of 10 integers (any values).
- Print the first three elements, the last three, and the middle four.
💡 Show Answer
package main
import "fmt"
func main() {
nums := []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
fmt.Println("First 3:", nums[:3])
fmt.Println("Last 3:", nums[7:])
fmt.Println("Middle 4:", nums[3:7])
}
4. The nil map panic
- Declare a map with
var m map[string]int(without initialising it). - Try to write to it:
m["key"] = 1. - Run the code. What happens?
- Fix it using
make.
Ask Copilot: Why does writing to a nil map panic in Go?
💡 Show Answer
The program panics with: assignment to entry in nil map. A var declaration gives the map a nil value — no memory is allocated for it. You must initialise it first:
m := make(map[string]int)
m["key"] = 1
fmt.Println(m) // map[key:1]
In Python, d = {} always creates a ready-to-use dictionary. Go separates declaration from initialisation, and using an uninitialised map is an error.
5. Check if a key exists
- Create a map of country codes to country names (at least five entries).
- Write code that looks up a code and prints the country name if found, or
"Unknown"if not.
💡 Show Answer
package main
import "fmt"
func main() {
countries := map[string]string{
"GB": "United Kingdom",
"US": "United States",
"JP": "Japan",
"DE": "Germany",
"FR": "France",
}
codes := []string{"GB", "AU", "JP", "ZZ"}
for _, code := range codes {
if name, ok := countries[code]; ok {
fmt.Printf("%s: %s\n", code, name)
} else {
fmt.Printf("%s: Unknown\n", code)
}
}
}
6. Word frequency counter
- Write a program that counts how many times each word appears in a sentence.
- Print the results sorted alphabetically by word.
Ask Copilot: How do I sort the keys of a map in Go?
💡 Show Answer
package main
import (
"fmt"
"sort"
"strings"
)
func main() {
sentence := "the quick brown fox jumps over the lazy dog the fox"
words := strings.Split(sentence, " ")
counts := make(map[string]int)
for _, word := range words {
counts[word]++
}
// Sort keys
keys := make([]string, 0, len(counts))
for k := range counts {
keys = append(keys, k)
}
sort.Strings(keys)
for _, word := range keys {
fmt.Printf("%-10s %d\n", word, counts[word])
}
}
7. Filter a slice
- Write a function
filter(nums []int, predicate func(int) bool) []intthat returns only the elements for which the predicate returnstrue. - Use it to find all even numbers in a slice.
💡 Show Answer
package main
import "fmt"
func filter(nums []int, predicate func(int) bool) []int {
result := []int{}
for _, n := range nums {
if predicate(n) {
result = append(result, n)
}
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
evens := filter(numbers, func(n int) bool { return n%2 == 0 })
fmt.Println(evens) // [2 4 6 8 10]
}
Ask Copilot: Does Go have built-in filter, map, and reduce functions like Python? The answer is instructive.
8. Group slice elements into a map
- Given a slice of words, create a map that groups them by their first letter.
- The map type should be
map[string][]string.
💡 Show Answer
package main
import "fmt"
func main() {
words := []string{"apple", "avocado", "banana", "blueberry", "cherry", "apricot", "cranberry"}
grouped := make(map[string][]string)
for _, word := range words {
key := string(word[0])
grouped[key] = append(grouped[key], word)
}
for letter, ws := range grouped {
fmt.Printf("%s: %v\n", letter, ws)
}
}