03 — Slices, maps, structs, and JSON

Goal

Represent tasks as Go data and convert them to JSON for a small command-line preview.

Step 1: Define a struct

Replace main.go:

package main

import (
    "encoding/json"
    "fmt"
)

type Task struct {
    ID     string `json:"id"`
    Title  string `json:"title"`
    Status string `json:"status"`
}

func main() {
    tasks := []Task{
        {ID: "task-1", Title: "Sketch the API", Status: "TODO"},
        {ID: "task-2", Title: "Build the screen", Status: "IN_PROGRESS"},
    }

    byID := make(map[string]Task)
    for _, task := range tasks {
        byID[task.ID] = task
    }

    selected, ok := byID["task-1"]
    if !ok {
        panic("task not found")
    }

    data, err := json.MarshalIndent(selected, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
}

Run gofmt -w main.go and go run .. Expected output is a formatted JSON object with id, title, and status.

Step 2: Understand the containers

A slice such as []Task is a growable sequence. append(tasks, item) returns the resulting slice. A map such as map[string]Task stores values by key. Map lookup returns a value and an ok flag; use the flag to distinguish a missing key from a zero-valued value. A struct groups related fields. JSON tags define wire names, and exported fields (capitalized names) are encoded by encoding/json.

Slices and maps are reference-like descriptors over underlying storage: copying one does not necessarily copy all its elements. That makes them efficient, but means two parts of a program can observe shared changes. When you need independent data, copy the slice or individual values deliberately. A map is not safe for simultaneous writes from multiple goroutines; later the in-memory repository protects its map with a mutex.

JSON is a text encoding often used by web APIs. gRPC in this course sends Protocol Buffer messages instead, but the same struct-design ideas apply: choose clear fields, keep identifiers stable, and distinguish missing data from meaningful zero values.

Step 3: Add a task

Before marshaling, append:

tasks = append(tasks, Task{ID: "task-3", Title: "Try the app", Status: "TODO"})

Rebuild byID after appending, then look up task-3 and print it. Observe that maps are not automatically kept in sync with the slice.

Checkpoint

Add a Description string field with JSON tag description. Create a task with a description and confirm it appears in JSON.

Practice

Write a function that returns all tasks whose status is TODO. Return a slice and leave the original slice unchanged.

Troubleshooting

If JSON prints {}, check that struct field names start with capitals. If lookup returns a zero task, check the ok result and the key spelling.

results matching ""

    No results matching ""