Code

Refactor main.go — counting and lookup

Two new powers for YOUR app, both built on maps. Item grows a Category field (Add now asks for it), and the menu gains 4) Stats by category and 5) Find by title — Add / List / Mark read stay.

Grow the struct and the Add flow:

type Item struct {
	Title     string
	Pages     int
	PagesRead int
	Read      bool
	Category  string // NEW
}
			fmt.Print("Category: ")
			category := readLine()
			items = append(items, Item{Title: title, Pages: pages, Category: category})

Counting by category — the m[k]++ pattern. One helper folds the slice into a category → count map:

func countByCategory(items []Item) map[string]int {
	counts := make(map[string]int)
	for _, it := range items {
		counts[it.Category]++ // a missing key starts at 0, then +1
	}
	return counts
}

No "have I seen this category yet?" check — a missing key reads as 0, so ++ just works. showStats (menu 4) ranges the counts and prints them. Remember step 2: that order is random.

Finding by title — comma-ok on a map[string]Item. Build the lookup once, then read it with the comma-ok idiom (menu 5):

func indexByTitle(items []Item) map[string]Item {
	index := make(map[string]Item, len(items))
	for _, it := range items {
		index[it.Title] = it
	}
	return index
}
		case "5":
			fmt.Print("Exact title: ")
			index := indexByTitle(items)
			if it, ok := index[readLine()]; ok {
				fmt.Printf("Found: %s — %d pages, %s\n", it.Title, it.Pages, it.Category)
			} else {
				fmt.Println("Not found.")
			}

The ok is what makes it safe: a missing title gives the zero Item{} with ok == false, so you print "Not found." instead of a blank record.

Verify — add three books in two categories, then stats and find:

> 1
Title: Dune
Pages: 412
Category: sci-fi
Added: Dune
... (add War and Peace / history, Hyperion / sci-fi the same way)

> 4
sci-fi       2
history      1

> 5
Exact title: Dune
Found: Dune — 412 pages, sci-fi

> 5
Exact title: Unknown Book
Not found.

Gotcha (random map order). Run 4 twice — the two category lines can swap order. Go deliberately randomizes range over a map so no one leans on an order that was never promised. Don't fight it; a map is for lookup and counting, not for order.