This commit is contained in:
wagslane
2023-05-01 15:25:27 -06:00
parent f8912668b8
commit 9be3074de6
868 changed files with 58698 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
package main
import (
"fmt"
)
func getUserMap(names []string, phoneNumbers []int) (map[string]user, error) {
// ?
}
// don't touch below this line
type user struct {
name string
phoneNumber int
}
func test(names []string, phoneNumbers []int) {
fmt.Println("Creating map...")
defer fmt.Println("====================================")
users, err := getUserMap(names, phoneNumbers)
if err != nil {
fmt.Println(err)
return
}
for _, name := range names {
fmt.Printf("key: %v, value:\n", name)
fmt.Println(" - name:", users[name].name)
fmt.Println(" - number:", users[name].phoneNumber)
}
}
func main() {
test(
[]string{"John", "Bob", "Jill"},
[]int{14355550987, 98765550987, 18265554567},
)
test(
[]string{"John", "Bob"},
[]int{14355550987, 98765550987, 18265554567},
)
test(
[]string{"George", "Sally", "Rich", "Sue"},
[]int{20955559812, 38385550982, 48265554567, 16045559873},
)
}

View File

@@ -0,0 +1,58 @@
package main
import (
"errors"
"fmt"
)
func getUserMap(names []string, phoneNumbers []int) (map[string]user, error) {
users := make(map[string]user)
if len(names) != len(phoneNumbers) {
return nil, errors.New("invalid sizes")
}
for i := range names {
name := names[i]
users[name] = user{
name: name,
phoneNumber: phoneNumbers[i],
}
}
return users, nil
}
// don't touch below this line
type user struct {
name string
phoneNumber int
}
func test(names []string, phoneNumbers []int) {
fmt.Println("Creating map...")
defer fmt.Println("====================================")
users, err := getUserMap(names, phoneNumbers)
if err != nil {
fmt.Println(err)
return
}
for _, name := range names {
fmt.Printf("key: %v, value:\n", name)
fmt.Println(" - name:", users[name].name)
fmt.Println(" - number:", users[name].phoneNumber)
}
}
func main() {
test(
[]string{"John", "Bob", "Jill"},
[]int{14355550987, 98765550987, 18265554567},
)
test(
[]string{"John", "Bob"},
[]int{14355550987, 98765550987, 18265554567},
)
test(
[]string{"George", "Sally", "Rich", "Sue"},
[]int{20955559812, 38385550982, 48265554567, 16045559873},
)
}

View File

@@ -0,0 +1,28 @@
Creating map...
key: John, value:
- name: John
- number: 14355550987
key: Bob, value:
- name: Bob
- number: 98765550987
key: Jill, value:
- name: Jill
- number: 18265554567
====================================
Creating map...
invalid sizes
====================================
Creating map...
key: George, value:
- name: George
- number: 20955559812
key: Sally, value:
- name: Sally
- number: 38385550982
key: Rich, value:
- name: Rich
- number: 48265554567
key: Sue, value:
- name: Sue
- number: 16045559873
====================================

View File

@@ -0,0 +1,41 @@
# Maps
Maps are similar to JavaScript objects, Python dictionaries, and Ruby hashes. Maps are a data structure that provides key->value mapping.
The zero value of a map is `nil`.
We can create a map by using a literal or by using the `make()` function:
```go
ages := make(map[string]int)
ages["John"] = 37
ages["Mary"] = 24
ages["Mary"] = 21 // overwrites 24
```
```go
ages = map[string]int{
"John": 37,
"Mary": 21,
}
```
The `len()` function works on a map, it returns the total number of key/value pairs.
```go
ages = map[string]int{
"John": 37,
"Mary": 21,
}
fmt.Println(len(ages)) // 2
```
## Assignment
We can speed up our contact-info lookups by using a map! Looking up a value in a map by its key is much faster than searching through a slice.
Complete the `getUserMap` function. It takes a slice of names and a slice of phone numbers, and returns a map of `name` -> `user` structs and potentially an `error`. A `user` struct just contains a user's name and phone number.
If the length of `names` and `phoneNumbers` is not equal, return an error with the string "invalid sizes".
The first name in the `names` slice matches the first phone number, and so on.

View File

@@ -0,0 +1,73 @@
package main
import (
"fmt"
"sort"
)
func deleteIfNecessary(users map[string]user, name string) (deleted bool, err error) {
// ?
}
// don't touch below this line
type user struct {
name string
number int
scheduledForDeletion bool
}
func test(users map[string]user, name string) {
fmt.Printf("Attempting to delete %s...\n", name)
defer fmt.Println("====================================")
deleted, err := deleteIfNecessary(users, name)
if err != nil {
fmt.Println(err)
return
}
if deleted {
fmt.Println("Deleted:", name)
return
}
fmt.Println("Did not delete:", name)
}
func main() {
users := map[string]user{
"john": {
name: "john",
number: 18965554631,
scheduledForDeletion: true,
},
"elon": {
name: "elon",
number: 19875556452,
scheduledForDeletion: true,
},
"breanna": {
name: "breanna",
number: 98575554231,
scheduledForDeletion: false,
},
"kade": {
name: "kade",
number: 10765557221,
scheduledForDeletion: false,
},
}
test(users, "john")
test(users, "musk")
test(users, "santa")
test(users, "kade")
keys := []string{}
for name := range users {
keys = append(keys, name)
}
sort.Strings(keys)
fmt.Println("Final map keys:")
for _, name := range keys {
fmt.Println(" - ", name)
}
}

View File

@@ -0,0 +1,82 @@
package main
import (
"errors"
"fmt"
"sort"
)
func deleteIfNecessary(users map[string]user, name string) (deleted bool, err error) {
user, ok := users[name]
if !ok {
return false, errors.New("not found")
}
if !user.scheduledForDeletion {
return false, nil
}
delete(users, name)
return true, nil
}
// don't touch below this line
type user struct {
name string
number int
scheduledForDeletion bool
}
func test(users map[string]user, name string) {
fmt.Printf("Attempting to delete %s...\n", name)
defer fmt.Println("====================================")
deleted, err := deleteIfNecessary(users, name)
if err != nil {
fmt.Println(err)
return
}
if deleted {
fmt.Println("Deleted:", name)
return
}
fmt.Println("Did not delete:", name)
}
func main() {
users := map[string]user{
"john": {
name: "john",
number: 18965554631,
scheduledForDeletion: true,
},
"elon": {
name: "elon",
number: 19875556452,
scheduledForDeletion: true,
},
"breanna": {
name: "breanna",
number: 98575554231,
scheduledForDeletion: false,
},
"kade": {
name: "kade",
number: 10765557221,
scheduledForDeletion: false,
},
}
test(users, "john")
test(users, "musk")
test(users, "santa")
test(users, "kade")
keys := []string{}
for name := range users {
keys = append(keys, name)
}
sort.Strings(keys)
fmt.Println("Final map keys:")
for _, name := range keys {
fmt.Println(" - ", name)
}
}

View File

@@ -0,0 +1,16 @@
Attempting to delete john...
Deleted: john
====================================
Attempting to delete musk...
not found
====================================
Attempting to delete santa...
not found
====================================
Attempting to delete kade...
Did not delete: kade
====================================
Final map keys:
- breanna
- elon
- kade

View File

@@ -0,0 +1,43 @@
# Mutations
## Insert and element
```go
m[key] = elem
```
## Get an element
```go
elem = m[key]
```
## Delete an element
```go
delete(m, key)
```
## Check if a key exists
```go
elem, ok := m[key]
```
If `key` is in `m`, then `ok` is `true`. If not, `ok` is `false`.
If `key` is not in the map, then `elem` is the zero value for the map's element type.
## Assignment
It's important to keep up with privacy regulations and to respect our user's data. We need a function that will delete user records.
Complete the `deleteIfNecessary` function.
* If the user doesn't exist in the map, return the error `not found`.
* If they exist but aren't scheduled for deletion, return `deleted` as `false` with no errors.
* If they exist and are scheduled for deletion, return `deleted` as `true` with no errors and delete their record from the map.
## Note on passing maps
Like slices, maps are also passed by reference into functions. This means that when a map is passed into a function we write, we *can* make changes to the original, we don't have a copy.

View File

@@ -0,0 +1,8 @@
{
"question": "What makes a type qualify to be used as a map key?",
"answers": [
"The type is comparable",
"The type is basic",
"The type is Go native; not custom"
]
}

View File

@@ -0,0 +1,54 @@
# Key Types
Any type can be used as the *value* in a map, but *keys* are more restrictive.
Read the following section of the official [Go blog](https://go.dev/blog/maps):
As mentioned earlier, **map keys may be of any type that is comparable**. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.
It's obvious that strings, ints, and other basic types should be available as map keys, but perhaps unexpected are struct keys. Struct can be used to key data by multiple dimensions. For example, this map of maps could be used to tally web page hits by country:
```go
hits := make(map[string]map[string]int)
```
This is map of string to (map of string to int). Each key of the outer map is the path to a web page with its own inner map. Each inner map key is a two-letter country code. This expression retrieves the number of times an Australian has loaded the documentation page:
```go
n := hits["/doc/"]["au"]
```
Unfortunately, this approach becomes unwieldy when adding data, as for any given outer key you must check if the inner map exists, and create it if needed:
```go
func add(m map[string]map[string]int, path, country string) {
mm, ok := m[path]
if !ok {
mm = make(map[string]int)
m[path] = mm
}
mm[country]++
}
add(hits, "/doc/", "au")
```
On the other hand, a design that uses a single map with a struct key does away with all that complexity:
```go
type Key struct {
Path, Country string
}
hits := make(map[Key]int)
```
When a Vietnamese person visits the home page, incrementing (and possibly creating) the appropriate counter is a one-liner:
```go
hits[Key{"/", "vn"}]++
```
And its similarly straightforward to see how many Swiss people have read the spec:
```go
n := hits[Key{"/ref/spec", "ch"}]
```

View File

@@ -0,0 +1,7 @@
{
"question": "Which is simpler?",
"answers": [
"To use a struct directly as a key",
"To nest maps"
]
}

View File

@@ -0,0 +1,54 @@
# Key Types
Any type can be used as the *value* in a map, but *keys* are more restrictive.
Read the following section of the official [Go blog](https://go.dev/blog/maps):
As mentioned earlier, **map keys may be of any type that is comparable**. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.
It's obvious that strings, ints, and other basic types should be available as map keys, but perhaps unexpected are struct keys. Struct can be used to key data by multiple dimensions. For example, this map of maps could be used to tally web page hits by country:
```go
hits := make(map[string]map[string]int)
```
This is map of string to (map of string to int). Each key of the outer map is the path to a web page with its own inner map. Each inner map key is a two-letter country code. This expression retrieves the number of times an Australian has loaded the documentation page:
```go
n := hits["/doc/"]["au"]
```
Unfortunately, this approach becomes unwieldy when adding data, as for any given outer key you must check if the inner map exists, and create it if needed:
```go
func add(m map[string]map[string]int, path, country string) {
mm, ok := m[path]
if !ok {
mm = make(map[string]int)
m[path] = mm
}
mm[country]++
}
add(hits, "/doc/", "au")
```
On the other hand, a design that uses a single map with a struct key does away with all that complexity:
```go
type Key struct {
Path, Country string
}
hits := make(map[Key]int)
```
When a Vietnamese person visits the home page, incrementing (and possibly creating) the appropriate counter is a one-liner:
```go
hits[Key{"/", "vn"}]++
```
And its similarly straightforward to see how many Swiss people have read the spec:
```go
n := hits[Key{"/ref/spec", "ch"}]
```

View File

@@ -0,0 +1,39 @@
package main
import (
"crypto/md5"
"fmt"
"io"
)
func getCounts(userIDs []string) map[string]int {
// ?
}
// don't edit below this line
func test(userIDs []string, ids []string) {
fmt.Printf("Generating counts for %v user IDs...\n", len(userIDs))
counts := getCounts(userIDs)
fmt.Println("Counts from select IDs:")
for _, k := range ids {
v := counts[k]
fmt.Printf(" - %s: %d\n", k, v)
}
fmt.Println("=====================================")
}
func main() {
userIDs := []string{}
for i := 0; i < 10000; i++ {
h := md5.New()
io.WriteString(h, fmt.Sprint(i))
key := fmt.Sprintf("%x", h.Sum(nil))
userIDs = append(userIDs, key[:2])
}
test(userIDs, []string{"00", "ff", "dd"})
test(userIDs, []string{"aa", "12", "32"})
test(userIDs, []string{"bb", "33"})
}

View File

@@ -0,0 +1,46 @@
package main
import (
"crypto/md5"
"fmt"
"io"
)
func getCounts(userIDs []string) map[string]int {
m := map[string]int{}
for _, id := range userIDs {
if _, ok := m[id]; !ok {
m[id] = 0
}
m[id]++
}
return m
}
// don't edit below this line
func test(userIDs []string, ids []string) {
fmt.Printf("Generating counts for %v user IDs...\n", len(userIDs))
counts := getCounts(userIDs)
fmt.Println("Counts from select IDs:")
for _, k := range ids {
v := counts[k]
fmt.Printf(" - %s: %d\n", k, v)
}
fmt.Println("=====================================")
}
func main() {
userIDs := []string{}
for i := 0; i < 10000; i++ {
h := md5.New()
io.WriteString(h, fmt.Sprint(i))
key := fmt.Sprintf("%x", h.Sum(nil))
userIDs = append(userIDs, key[:2])
}
test(userIDs, []string{"00", "ff", "dd"})
test(userIDs, []string{"aa", "12", "32"})
test(userIDs, []string{"bb", "33"})
}

View File

@@ -0,0 +1,17 @@
Generating counts for 10000 user IDs...
Counts from select IDs:
- 00: 31
- ff: 27
- dd: 37
=====================================
Generating counts for 10000 user IDs...
Counts from select IDs:
- aa: 29
- 12: 29
- 32: 41
=====================================
Generating counts for 10000 user IDs...
Counts from select IDs:
- bb: 28
- 33: 46
=====================================

View File

@@ -0,0 +1,19 @@
# Count Instances
Remember that you can check if a key is already present in a map by using the second return value from the index operation.
```go
names := map[string]int{}
if _, ok := names["elon"]; !ok {
// if the key doesn't exist yet,
// initialize its value to 0
names["elon"] = 0
}
```
## Assignment
We have a slice of user ids, and each instance of an id in the slice indicates that a message was sent to that user. We need to count up how many times each user's id appears in the slice to track how many messages they received.
Implement the `getCounts` function. It should return a map of `string -> int` so that each `int` is a count of how many times each string was found in the slice.

View File

@@ -0,0 +1,9 @@
{
"question": "Maps can have at most ____ value(s) associated with the same key",
"answers": [
"1",
"2",
"3",
"any number of"
]
}

View File

@@ -0,0 +1,65 @@
# Effective Go
Read the following paraphrased sections from [effective Go regarding maps](https://go.dev/doc/effective_go#maps):
## Like slices, maps hold references
Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
## Map literals
Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
```go
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}
```
## Missing keys
An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
```go
attended := map[string]bool{
"Ann": true,
"Joe": true,
...
}
if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}
```
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.
```go
var seconds int
var ok bool
seconds, ok = timeZone[tz]
```
For obvious reasons, this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
```go
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
```
## Deleting map entries
To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this even if the key is already absent from the map.
```go
delete(timeZone, "PDT") // Now on Standard Time
```

View File

@@ -0,0 +1,8 @@
{
"question": "Attempting to get a value from a map where the key doesn't exist...",
"answers": [
"Returns the zero value",
"Panics",
"Returns the closest value"
]
}

View File

@@ -0,0 +1,65 @@
# Effective Go
Read the following paraphrased sections from [effective Go regarding maps](https://go.dev/doc/effective_go#maps):
## Like slices, maps hold references
Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
## Map literals
Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
```go
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}
```
## Missing keys
An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
```go
attended := map[string]bool{
"Ann": true,
"Joe": true,
...
}
if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}
```
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.
```go
var seconds int
var ok bool
seconds, ok = timeZone[tz]
```
For obvious reasons, this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
```go
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
```
## Deleting map entries
To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this even if the key is already absent from the map.
```go
delete(timeZone, "PDT") // Now on Standard Time
```

View File

@@ -0,0 +1,7 @@
{
"question": "A function can mutate the values stored in a map and those changes ____ the caller",
"answers": [
"affect",
"do not affect"
]
}

View File

@@ -0,0 +1,65 @@
# Effective Go
Read the following paraphrased sections from [effective Go regarding maps](https://go.dev/doc/effective_go#maps):
## Like slices, maps hold references
Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
## Map literals
Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
```go
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}
```
## Missing keys
An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
```go
attended := map[string]bool{
"Ann": true,
"Joe": true,
...
}
if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}
```
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.
```go
var seconds int
var ok bool
seconds, ok = timeZone[tz]
```
For obvious reasons, this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
```go
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
```
## Deleting map entries
To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this even if the key is already absent from the map.
```go
delete(timeZone, "PDT") // Now on Standard Time
```

View File

@@ -0,0 +1,8 @@
{
"question": "What does the second return value from a retrieve operation in a map indicate?",
"answers": [
"A boolean that indicates whether the key exists",
"A boolean that indicates whether the value at the key is a nil value",
"A boolean that indicates whether the value at the key is a zero value"
]
}

View File

@@ -0,0 +1,65 @@
# Effective Go
Read the following paraphrased sections from [effective Go regarding maps](https://go.dev/doc/effective_go#maps):
## Like slices, maps hold references
Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
## Map literals
Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
```go
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}
```
## Missing keys
An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
```go
attended := map[string]bool{
"Ann": true,
"Joe": true,
...
}
if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}
```
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.
```go
var seconds int
var ok bool
seconds, ok = timeZone[tz]
```
For obvious reasons, this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
```go
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
```
## Deleting map entries
To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this even if the key is already absent from the map.
```go
delete(timeZone, "PDT") // Now on Standard Time
```

View File

@@ -0,0 +1,39 @@
package main
import (
"fmt"
)
func getNameCounts(names []string) map[rune]map[string]int {
// ?
}
// don't edit below this line
func test(names []string, initial rune, name string) {
fmt.Printf("Generating counts for %v names...\n", len(names))
nameCounts := getNameCounts(names)
count := nameCounts[initial][name]
fmt.Printf("Count for [%c][%s]: %d\n", initial, name, count)
fmt.Println("=====================================")
}
func main() {
test(getNames(50), 'M', "Matthew")
test(getNames(100), 'G', "George")
test(getNames(150), 'D', "Drew")
test(getNames(200), 'P', "Philip")
test(getNames(250), 'B', "Bryant")
test(getNames(300), 'M', "Matthew")
}
func getNames(length int) []string {
names := []string{
"Grant", "Eduardo", "Peter", "Matthew", "Matthew", "Matthew", "Peter", "Peter", "Henry", "Parker", "Parker", "Parker", "Collin", "Hayden", "George", "Bradley", "Mitchell", "Devon", "Ricardo", "Shawn", "Taylor", "Nicolas", "Gregory", "Francisco", "Liam", "Kaleb", "Preston", "Erik", "Alexis", "Owen", "Omar", "Diego", "Dustin", "Corey", "Fernando", "Clayton", "Carter", "Ivan", "Jaden", "Javier", "Alec", "Johnathan", "Scott", "Manuel", "Cristian", "Alan", "Raymond", "Brett", "Max", "Drew", "Andres", "Gage", "Mario", "Dawson", "Dillon", "Cesar", "Wesley", "Levi", "Jakob", "Chandler", "Martin", "Malik", "Edgar", "Sergio", "Trenton", "Josiah", "Nolan", "Marco", "Drew", "Peyton", "Harrison", "Drew", "Hector", "Micah", "Roberto", "Drew", "Brady", "Erick", "Conner", "Jonah", "Casey", "Jayden", "Edwin", "Emmanuel", "Andre", "Phillip", "Brayden", "Landon", "Giovanni", "Bailey", "Ronald", "Braden", "Damian", "Donovan", "Ruben", "Frank", "Gerardo", "Pedro", "Andy", "Chance", "Abraham", "Calvin", "Trey", "Cade", "Donald", "Derrick", "Payton", "Darius", "Enrique", "Keith", "Raul", "Jaylen", "Troy", "Jonathon", "Cory", "Marc", "Eli", "Skyler", "Rafael", "Trent", "Griffin", "Colby", "Johnny", "Chad", "Armando", "Kobe", "Caden", "Marcos", "Cooper", "Elias", "Brenden", "Israel", "Avery", "Zane", "Zane", "Zane", "Zane", "Dante", "Josue", "Zackary", "Allen", "Philip", "Mathew", "Dennis", "Leonardo", "Ashton", "Philip", "Philip", "Philip", "Julio", "Miles", "Damien", "Ty", "Gustavo", "Drake", "Jaime", "Simon", "Jerry", "Curtis", "Kameron", "Lance", "Brock", "Bryson", "Alberto", "Dominick", "Jimmy", "Kaden", "Douglas", "Gary", "Brennan", "Zachery", "Randy", "Louis", "Larry", "Nickolas", "Albert", "Tony", "Fabian", "Keegan", "Saul", "Danny", "Tucker", "Myles", "Damon", "Arturo", "Corbin", "Deandre", "Ricky", "Kristopher", "Lane", "Pablo", "Darren", "Jarrett", "Zion", "Alfredo", "Micheal", "Angelo", "Carl", "Oliver", "Kyler", "Tommy", "Walter", "Dallas", "Jace", "Quinn", "Theodore", "Grayson", "Lorenzo", "Joe", "Arthur", "Bryant", "Roman", "Brent", "Russell", "Ramon", "Lawrence", "Moises", "Aiden", "Quentin", "Jay", "Tyrese", "Tristen", "Emanuel", "Salvador", "Terry", "Morgan", "Jeffery", "Esteban", "Tyson", "Braxton", "Branden", "Marvin", "Brody", "Craig", "Ismael", "Rodney", "Isiah", "Marshall", "Maurice", "Ernesto", "Emilio", "Brendon", "Kody", "Eddie", "Malachi", "Abel", "Keaton", "Jon", "Shaun", "Skylar", "Ezekiel", "Nikolas", "Santiago", "Kendall", "Axel", "Camden", "Trevon", "Bobby", "Conor", "Jamal", "Lukas", "Malcolm", "Zackery", "Jayson", "Javon", "Roger", "Reginald", "Zachariah", "Desmond", "Felix", "Johnathon", "Dean", "Quinton", "Ali", "Davis", "Gerald", "Rodrigo", "Demetrius", "Billy", "Rene", "Reece", "Kelvin", "Leo", "Justice", "Chris", "Guillermo", "Matthew", "Matthew", "Matthew", "Kevon", "Steve", "Frederick", "Clay", "Weston", "Dorian", "Hugo", "Roy", "Orlando", "Terrance", "Kai", "Khalil", "Khalil", "Khalil", "Graham", "Noel", "Willie", "Nathanael", "Terrell", "Tyrone",
}
if length > len(names) {
length = len(names)
}
return names[:length]
}

View File

@@ -0,0 +1,54 @@
package main
import (
"fmt"
)
func getNameCounts(names []string) map[rune]map[string]int {
nameCounts := make(map[rune]map[string]int)
for _, name := range names {
firstLetter := rune(0)
if len(name) != 0 {
firstLetter = rune(name[0])
}
if _, ok := nameCounts[firstLetter]; !ok {
nameCounts[firstLetter] = make(map[string]int)
}
if _, ok := nameCounts[firstLetter][name]; !ok {
nameCounts[firstLetter][name] = 0
}
nameCounts[firstLetter][name]++
}
return nameCounts
}
// don't edit below this line
func test(names []string, initial rune, name string) {
fmt.Printf("Generating counts for %v names...\n", len(names))
nameCounts := getNameCounts(names)
count := nameCounts[initial][name]
fmt.Printf("Count for [%c][%s]: %d\n", initial, name, count)
fmt.Println("=====================================")
}
func main() {
test(getNames(50), 'M', "Matthew")
test(getNames(100), 'G', "George")
test(getNames(150), 'D', "Drew")
test(getNames(200), 'P', "Philip")
test(getNames(250), 'B', "Bryant")
test(getNames(300), 'M', "Matthew")
}
func getNames(length int) []string {
names := []string{
"Grant", "Eduardo", "Peter", "Matthew", "Matthew", "Matthew", "Peter", "Peter", "Henry", "Parker", "Parker", "Parker", "Collin", "Hayden", "George", "Bradley", "Mitchell", "Devon", "Ricardo", "Shawn", "Taylor", "Nicolas", "Gregory", "Francisco", "Liam", "Kaleb", "Preston", "Erik", "Alexis", "Owen", "Omar", "Diego", "Dustin", "Corey", "Fernando", "Clayton", "Carter", "Ivan", "Jaden", "Javier", "Alec", "Johnathan", "Scott", "Manuel", "Cristian", "Alan", "Raymond", "Brett", "Max", "Drew", "Andres", "Gage", "Mario", "Dawson", "Dillon", "Cesar", "Wesley", "Levi", "Jakob", "Chandler", "Martin", "Malik", "Edgar", "Sergio", "Trenton", "Josiah", "Nolan", "Marco", "Drew", "Peyton", "Harrison", "Drew", "Hector", "Micah", "Roberto", "Drew", "Brady", "Erick", "Conner", "Jonah", "Casey", "Jayden", "Edwin", "Emmanuel", "Andre", "Phillip", "Brayden", "Landon", "Giovanni", "Bailey", "Ronald", "Braden", "Damian", "Donovan", "Ruben", "Frank", "Gerardo", "Pedro", "Andy", "Chance", "Abraham", "Calvin", "Trey", "Cade", "Donald", "Derrick", "Payton", "Darius", "Enrique", "Keith", "Raul", "Jaylen", "Troy", "Jonathon", "Cory", "Marc", "Eli", "Skyler", "Rafael", "Trent", "Griffin", "Colby", "Johnny", "Chad", "Armando", "Kobe", "Caden", "Marcos", "Cooper", "Elias", "Brenden", "Israel", "Avery", "Zane", "Zane", "Zane", "Zane", "Dante", "Josue", "Zackary", "Allen", "Philip", "Mathew", "Dennis", "Leonardo", "Ashton", "Philip", "Philip", "Philip", "Julio", "Miles", "Damien", "Ty", "Gustavo", "Drake", "Jaime", "Simon", "Jerry", "Curtis", "Kameron", "Lance", "Brock", "Bryson", "Alberto", "Dominick", "Jimmy", "Kaden", "Douglas", "Gary", "Brennan", "Zachery", "Randy", "Louis", "Larry", "Nickolas", "Albert", "Tony", "Fabian", "Keegan", "Saul", "Danny", "Tucker", "Myles", "Damon", "Arturo", "Corbin", "Deandre", "Ricky", "Kristopher", "Lane", "Pablo", "Darren", "Jarrett", "Zion", "Alfredo", "Micheal", "Angelo", "Carl", "Oliver", "Kyler", "Tommy", "Walter", "Dallas", "Jace", "Quinn", "Theodore", "Grayson", "Lorenzo", "Joe", "Arthur", "Bryant", "Roman", "Brent", "Russell", "Ramon", "Lawrence", "Moises", "Aiden", "Quentin", "Jay", "Tyrese", "Tristen", "Emanuel", "Salvador", "Terry", "Morgan", "Jeffery", "Esteban", "Tyson", "Braxton", "Branden", "Marvin", "Brody", "Craig", "Ismael", "Rodney", "Isiah", "Marshall", "Maurice", "Ernesto", "Emilio", "Brendon", "Kody", "Eddie", "Malachi", "Abel", "Keaton", "Jon", "Shaun", "Skylar", "Ezekiel", "Nikolas", "Santiago", "Kendall", "Axel", "Camden", "Trevon", "Bobby", "Conor", "Jamal", "Lukas", "Malcolm", "Zackery", "Jayson", "Javon", "Roger", "Reginald", "Zachariah", "Desmond", "Felix", "Johnathon", "Dean", "Quinton", "Ali", "Davis", "Gerald", "Rodrigo", "Demetrius", "Billy", "Rene", "Reece", "Kelvin", "Leo", "Justice", "Chris", "Guillermo", "Matthew", "Matthew", "Matthew", "Kevon", "Steve", "Frederick", "Clay", "Weston", "Dorian", "Hugo", "Roy", "Orlando", "Terrance", "Kai", "Khalil", "Khalil", "Khalil", "Graham", "Noel", "Willie", "Nathanael", "Terrell", "Tyrone",
}
if length > len(names) {
length = len(names)
}
return names[:length]
}

View File

@@ -0,0 +1,18 @@
Generating counts for 50 names...
Count for [M][Matthew]: 3
=====================================
Generating counts for 100 names...
Count for [G][George]: 1
=====================================
Generating counts for 150 names...
Count for [D][Drew]: 4
=====================================
Generating counts for 200 names...
Count for [P][Philip]: 4
=====================================
Generating counts for 250 names...
Count for [B][Bryant]: 1
=====================================
Generating counts for 300 names...
Count for [M][Matthew]: 6
=====================================

View File

@@ -0,0 +1,38 @@
# Nested
Maps can contain maps, creating a nested structure. For example:
```go
map[string]map[string]int
map[rune]map[string]int
map[int]map[string]map[string]int
```
## Assignment
Because Textio is a glorified customer database, we have a lot of internal logic for sorting and dealing with customer names.
Complete the `getNameCounts` function. It takes a slice of strings (names) and returns a nested map where the first key is all the unique first characters of the names, the second key is all the names themselves, and the value is the count of each name.
For example:
```
billy
billy
bob
joe
```
Creates the following nested map:
```
b: {
billy: 2,
bob: 1
},
j: {
joe: 1
}
```
Note that the test suite is *not* printing the map you're returning directly, but instead checking some specific keys.