Capstone: CSV sales summary
End of the Collections line. Every lesson in this unit took one job away from PHP's array and gave it to a Go type. This capstone puts them all back on the same train: a day's ticket machine log goes in, and a summary comes out.
7 sales (1 skipped)
Top stations:
1. Bank £18.00
2. Oval £18.00
3. Angel £8.40
Tickets: return, single, travelcard
The log is a small CSV, with a header and then one sale per line: station, ticket type, and price in pence. Like any real export, it's a little messy. There's a blank line, and one row where somebody typed "two pounds eighty" into the price column.
How it's put together
Like the Unit 1 capstone, there are two packages. sales/sales.go does the work in four functions, and main.go feeds it today's log and lays out the report. The layout is already written, so you only write the four functions. Here's how data flows through them:
flowchart TB accTitle: How the sales summary is built accDescr: The CSV text goes into sales.Parse, which returns a slice of Sale structs and a count of skipped rows. That slice feeds two functions. sales.ByStation adds up a map of pence per station, which sales.Top turns into a sorted slice of the top three Totals. sales.Tickets returns the distinct ticket types as a sorted slice of strings. The report combines the counts, the top stations and the ticket types. log["today's log, CSV text"] --> parse["sales.Parse<br/>[]Sale, skipped"] parse --> bs["sales.ByStation<br/>map[string]int"] bs --> top["sales.Top<br/>[]Total, sorted"] parse --> tk["sales.Tickets<br/>[]string"] top --> rep["report"] tk --> rep
What you'll use
| You'll need | From |
|---|---|
A slice built with append, and lines[1:] to skip the header |
2.2 |
| A map that adds up totals, made before it's written to | 2.4 |
| A slice of structs, built from a map | 2.5, 2.7 |
slices.SortFunc, cmp.Compare and cmp.Or for busiest first, ties A to Z |
2.6 |
slices.Sort then slices.Compact to remove duplicates |
2.6 |
strings.Split, strings.TrimSpace and strconv.Atoi |
1.5, 1.C |
The one function you might not have met is slices.Compact, which removes neighbouring duplicates. That's why Tickets sorts first: sorted, every duplicate sits next to its twin. The Reference panel under the task has its docs, along with strings.Split and strings.TrimSpace.
Tip
Start with the easy wins. ByStation and Tickets are a few lines each. Top is 2.6's leaderboard again. Leave Parse until last: it has the most rules, and the tests list every row it should keep or skip. Run's report stays empty until Parse works, because everything starts from its output, but the tests feed each function its own data, so the others can go green first.
Laravel corner
In Laravel this is a Collection chain: collect($rows)->groupBy('station')->map->sum('pence')->sortDesc()->take(3). The Go version spells each step out as a function with typed inputs and outputs. It's longer, but each piece is small, testable on its own, and impossible to feed the wrong shape of data.
Mind the gap
PHP has str_getcsv and Go has encoding/csv, and neither is used here. A real CSV can have quoted fields with commas inside them ("King's Cross, St Pancras"), and splitting on "," would break those. This log is simple enough for strings.Split. For real files, use encoding/csv, which reads from the readers you'll meet in lesson 7.4.
When it's all green, the Collections line is done. Unit 3 turns structs into something more: methods, interfaces and the Go take on classes.