-4

I want to count geolocations of IP addresses by country and then by city. The expected output would be something like:

$ cat ips.txt | ./geoips
United States  42
    Washington 21
    New York   21
China          10
    Beijing    10

I came up with this code that uses map of maps to keep the counts:

func main() {
    ips := parseIPs(getIPs())

    counts := make(map[string]map[string]int)

    for _, ip := range ips {
        g := &checkip.Geo{}
        _, err := g.Check(ip)
        if err != nil {
            log.Printf("while getting geolocation: %v", err)
            continue
        }
        counts[g.Country][g.City]++
    }

    fmt.Printf("%v\n", counts)
}

When I build and run it a get an error:

$ cat ips.txt | ./geoips
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
    /Users/xxx/geoips/main.go:30 +0x245

I understand the problem is that the 2nd map is not initialised. How do I do that?

Or is there a better way to keep the counts?

jreisinger
  • 1,493
  • 1
  • 10
  • 21

1 Answers1

0

Ok, I think got it:

        // The following three lines fix the "assignment
        // to entry in nil map" runtime error.
        if _, ok := counts[g.Country]; !ok {
            counts[g.Country] = make(map[string]int)
        }
        counts[g.Country][g.City]++
jreisinger
  • 1,493
  • 1
  • 10
  • 21
  • 2
    This has a bug : if a country has multiple cities, you will recreate the country's map everytime, deleting everything that has been done before. A better way is to check if `counts[g.Country] == nil` : if it is, make the map[string]int. Otherwise, you can just add into it. – Nathanael C. Jul 28 '21 at 13:14
  • thanks @NathanaelC., fixed. although I used `ok` instead of `nil` – jreisinger Jul 28 '21 at 13:35