-
Notifications
You must be signed in to change notification settings - Fork 0
excercises solutions #3
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| ) | ||
|
|
||
|
|
||
| type UserResponse struct { | ||
| Attributes User `json:"attributes"` | ||
| } | ||
|
|
||
| // here its used by json := exported | ||
| type User struct { | ||
| ID int `json:"id"` | ||
| Name string `json:"name"` | ||
| AccountIDs []int `json:"account_ids"` | ||
| } | ||
| // array_of_ints = {array_of_strings}.map(:to_s) | ||
| // convert an array into something else | ||
| // 1. create a second array variable | ||
| // arrayOfInts := make([]int, len(arrayOfStrings)) | ||
| // 2. iterate and convert each element | ||
| // for i, v := range arrayOfStrings { | ||
| // arrayOfInts[i] = .... | ||
| // } | ||
|
|
||
| func main() { | ||
| url := "https://sample-accounts-api.herokuapp.com/users/1" | ||
| // no underscore in golang | ||
| resp, err := http.Get(url) | ||
| // best practice to check and write defer code nearby to original code | ||
| defer resp.Body.Close() // defer here vs derer after error handling is same in this case | ||
|
|
||
| if err != nil { | ||
| fmt.Println("error found %v", err) // shadowing error variable... [ from interface error{}] -> avoid naming conflicts with packages | ||
| return | ||
| } | ||
| // return fmt.Errorf("http get %w", err) | ||
| //error handlling missing | ||
|
|
||
| // defer resp.Body.Close() | ||
| // defer will be skipped here, but it's ok because an error occurred, so there is no body to close | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| fmt.Println("error found %v", err) | ||
| return | ||
| } | ||
|
|
||
| // var result map[string]any | ||
| // err = json.Unmarshal(body, &result) | ||
|
|
||
| // if err != nil { | ||
| // fmt.Println("error found") | ||
| // } | ||
| // const attr = "attributes" | ||
| // optimization + safety [ type safety ] | ||
| // attributes, _ := result["attributes"].(map[string]any) | ||
| // attributesJSON, _ := json.Marshal(attributes) | ||
| var user UserResponse // dont use same var name as struct type name | ||
| err = json.Unmarshal(body, &user) | ||
|
|
||
| if err != nil { | ||
| fmt.Println("error found") | ||
| } | ||
|
|
||
| fmt.Println("User: ", user.Attributes.AccountIDs) | ||
| // fmt.Printf("ID: %d, Account IDs: %v\n", user.ID, user.AccountIDs) | ||
|
|
||
| // user.Attributes.AccountIDs is slice of ints | ||
| // convert to slice of strings by using strconv.Itoa | ||
| // use strings.Join() to join the elements with "," | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| ) | ||
|
|
||
| type UserResponse struct { | ||
| Attributes User `json:"attributes"` | ||
| } | ||
|
|
||
| type User struct { | ||
| ID int `json:"id"` | ||
| Name string `json:"name"` | ||
| AccountIDs []int `json:"account_ids"` | ||
| } | ||
|
|
||
| type AccountResponse struct { | ||
| Attributes Account `json:"attributes"` | ||
| } | ||
|
|
||
| type Account struct { | ||
| ID int `json:"id"` | ||
| UserID int `json:"user_id"` | ||
sidharthchadha2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Name string `json:"name"` | ||
| Balance int `json:"balance"` | ||
| } | ||
|
|
||
| func getUser(userID int) (User, error) { | ||
| url := fmt.Sprintf("https://sample-accounts-api.herokuapp.com/users/%d", userID) | ||
| resp, err := http.Get(url) | ||
| if err != nil { | ||
| return User{}, err | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function has great error handling! But it is generally recommended to use error wrapping (nested errors) when you propagate errors to parent functions. This way, you can immediately identify where an error comes from, just by looking at the error message. Error wrapping is provided by the In this case, instead of returning the returnUser{}, fmt.Errorf("fetching user %v: %w", userID, err)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ohhh i see , the error stack will have 2 similar logs this way..gotcha |
||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return User{}, err | ||
| } | ||
|
|
||
| var userResponse UserResponse | ||
| err = json.Unmarshal(body, &userResponse) | ||
| if err != nil { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comes down to personal preferences, so other people may have different suggestions on this. But a more idiomatic way of retrieving an error and checking if it's nil, is like this: if err = json.Unmarshal(body, &userResponse); err != nil {
return User{}, err
}The main benefit of this approach is that the scope of the Note: your solution is also perfectly valid, since the |
||
| return User{}, err | ||
| } | ||
|
|
||
| return userResponse.Attributes, nil | ||
| } | ||
|
|
||
| func getAccounts(userID int) ([]Account, error) { | ||
| url := fmt.Sprintf("https://sample-accounts-api.herokuapp.com/users/%d/accounts", userID) | ||
| resp, err := http.Get(url) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| body, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var accountResponses []AccountResponse | ||
| err = json.Unmarshal(body, &accountResponses) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| accounts := make([]Account, len(accountResponses)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great use of the |
||
| for i, accountResponse := range accountResponses { | ||
| accounts[i] = accountResponse.Attributes | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick; your solution works great, but there is a safer solution that I would recommend. In this file, you have already created a slice accounts := make([]Account, len(accountResponses))
for i, accountResponse := range accountResponses {
accounts[i] = accountResponse.Attributes
}This will work great if you are 100% sure that the capacity is correct, and the slice does not need to be resized. However, the code will cause panics if the capacity is not big enough. A more common approach, is to use accounts := make([]Account, len(accountResponses))
for i, accountResponse := range accountResponses {
accounts = append(accounts, accountResponse.Attributes)
}There are some online articles that compare the performance between assignments |
||
| } | ||
|
|
||
| return accounts, nil | ||
| } | ||
|
|
||
| func displayUser(user User, accounts []Account) { | ||
| fmt.Printf("User: %s\n", user.Name) | ||
| fmt.Println("Accounts:") | ||
| totalBalance := 0 | ||
| for _, account := range accounts { | ||
| fmt.Printf(" - %s: %d\n", account.Name, account.Balance) | ||
| totalBalance += account.Balance | ||
| } | ||
| fmt.Printf("Total Balance: %d\n", totalBalance) | ||
| } | ||
|
|
||
| func main() { | ||
sidharthchadha2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var userID int | ||
| _, err := fmt.Scanln(&userID) | ||
| if err != nil { | ||
| fmt.Println("Invalid input") | ||
| return | ||
| } | ||
|
|
||
| user, err := getUser(userID) | ||
| if err != nil { | ||
| fmt.Println("error found") | ||
| return | ||
| } | ||
|
|
||
| accounts, err := getAccounts(userID) | ||
| if err != nil { | ||
| fmt.Println("error found") | ||
| return | ||
| } | ||
|
|
||
| displayUser(user, accounts) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package stringutils | ||
|
|
||
| import ( | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestReverse(t *testing.T) { | ||
sidharthchadha2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| testCases := []struct { | ||
| name string | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file has a mix of whitespaces and tabs, if you auto-format the file in your IDE, it will be fixed 👌 (Go only uses |
||
| input string | ||
| expected string | ||
| }{ | ||
| {"empty string", "", ""}, | ||
| {"single character", "x", "x"}, | ||
| {"palindrome", "aba", "aba"}, | ||
| {"simple string", "qwe", "ewq"}, | ||
| {"with spaces", "qwe rt", "tr ewq"}, | ||
| {"with numbers", "12345", "54321"}, | ||
| {"with special characters", "!@#$%^&*()", ")(*&^%$#@!"}, | ||
| {"with mixed case", "Hello World", "dlroW olleH"}, | ||
| {"with unicode", "こんにちは", "はちにんこ"}, | ||
| {"with emojis", "😀😃😄", "😄😃😀"}, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| actual := Reverse(tc.input) | ||
| if actual != tc.expected { | ||
| t.Errorf("Reverse(%q) = %q; expected %q", tc.input, actual, tc.expected) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.