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
4twice — the two category lines can swap order. Go deliberately randomizesrangeover 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.