CodingNeed.

3 · Reliability and project · 45 MIN

Project · Validate and summarize expenses

Combine validated input, grouping and deterministic reporting.

This project accepts a small JSON array and groups nonnegative whole-unit expenses. Decoding establishes syntax; validation establishes domain rules; sorting makes output stable. The example stops at the first invalid record rather than publishing a partial report. A production importer must also bound request bytes, numeric ranges and record counts. Choose the contract before optimizing: accepting a refund, for example, changes the meaning of the negative-amount check and requires new tests.

Keep report pure enough to test without invoking main.

Read the example

package main
import ("encoding/json"; "fmt"; "sort")
type Expense struct { Category string; Amount int }
func report(raw []byte) (map[string]int, error) {
  var rows []Expense
  if err := json.Unmarshal(raw, &rows); err != nil { return nil, err }
  totals := make(map[string]int)
  for _, row := range rows {
    if row.Category == "" || row.Amount < 0 { return nil, fmt.Errorf("invalid expense") }
    totals[row.Category] += row.Amount
  }
  return totals, nil
}
func main() {
  totals, err := report([]byte(`[{"Category":"food","Amount":5},{"Category":"food","Amount":7}]`))
  if err != nil { fmt.Println("invalid"); return }
  keys := make([]string,0,len(totals)); for key := range totals { keys=append(keys,key) }; sort.Strings(keys)
  for _, key := range keys { fmt.Println(key, totals[key]) }
}
Check the expected output
food 12

Your challenge

Create table-driven tests in main_test.go and extend the importer to reject unknown JSON fields, oversized input and overflowing totals.

Solution cost: O(n + k log k) including sorted output for k categories. time · Account for collection storage separately from the returned result. space

Common trap

Valid JSON alone does not establish a safe business record.

Study the project implementation
package main
import ("encoding/json"; "fmt"; "sort")
type Expense struct { Category string; Amount int }
func report(raw []byte) (map[string]int, error) {
  var rows []Expense
  if err := json.Unmarshal(raw, &rows); err != nil { return nil, err }
  totals := make(map[string]int)
  for _, row := range rows {
    if row.Category == "" || row.Amount < 0 { return nil, fmt.Errorf("invalid expense") }
    totals[row.Category] += row.Amount
  }
  return totals, nil
}
func main() {
  totals, err := report([]byte(`[{"Category":"food","Amount":5},{"Category":"food","Amount":7}]`))
  if err != nil { fmt.Println("invalid"); return }
  keys := make([]string,0,len(totals)); for key := range totals { keys=append(keys,key) }; sort.Strings(keys)
  for _, key := range keys { fmt.Println(key, totals[key]) }
}

Further reading: Official documentation

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.