Finished maps

drew/sql-it
Drew Bednar 8 months ago
parent 809a8faf67
commit 5bd4222342

@ -0,0 +1,22 @@
# Maps
https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/maps
Build our own map
## Gotcha
A gotcha with maps is that they can be a nil value. A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic. You can read more about maps here.
Therefore, you should never initialize an empty map variable:
var m map[string]string
Instead, you can initialize an empty map like we were doing above, or use the make keyword to create a map for you:
var dictionary = map[string]string{}
// OR
var dictionary = make(map[string]string)

@ -0,0 +1,74 @@
package main
// these are sentinel errors. Look at article in pointers_errors dictory README.md
// var (
// ErrNotFound = errors.New("could not find word you were looking for")
// ErrWordExists = errors.New("key already exists, Add failed")
// )
// Instead we implement our own type that implements the error interface
// This has more benefits because you can do type checks instead of comparing error strings
// or sentinel errors. See the article https://dave.cheney.net/2016/04/07/constant-errors
const (
ErrNotFound = DictionaryErr("could not find word you were looking for")
ErrWordExists = DictionaryErr("key already exists, Add failed")
ErrWordDoesNotExist = DictionaryErr("key already exists, Add failed")
)
type DictionaryErr string
func (e DictionaryErr) Error() string {
return string(e)
}
type Dictionary map[string]string
func (d Dictionary) Search(word string) (string, error) {
val, ok := d[word]
if !ok {
return val, ErrNotFound
}
return val, nil
}
// Ok...this works because a map value is a pointer to a runtime.hmap structure
// So when you pass the map reference to this function it's actually copying the
// pointer so no need to &d get mem pointer.
func (d Dictionary) Add(word, definition string) error {
_, ok := d[word]
if ok {
return ErrWordExists
}
d[word] = definition
return nil
// The authors alt implementation
// _, err := d.Search(word)
// switch err {
// case ErrNotFound:
// d[word] = definition
// case nil:
// return ErrWordExists
// default:
// return err
// }
// return nil
}
func (d Dictionary) Update(word, definition string) error {
_, ok := d[word]
if !ok {
return ErrWordDoesNotExist
}
d[word] = definition
return nil
}
func (d Dictionary) Delete(word string) {
//Go has a built-in function delete that works on maps. It takes two arguments. The first is the map and the second is the key to be removed.
delete(d, word)
}

@ -0,0 +1,111 @@
package main
import (
"testing"
)
func assertError(t testing.TB, got, want error) {
t.Helper()
if got != want {
t.Errorf("got error %q want %q", got, want)
}
}
func assertStrings(t testing.TB, got, want string) {
t.Helper()
if got != want {
t.Errorf("got %q want %q give %q", got, want, "test")
}
}
func TestSearch(t *testing.T) {
searchStr := "this is just a test"
// dictionary := map[string]string{"test": searchStr}
// use our own type
dictionary := Dictionary{"test": searchStr}
t.Run("known word", func(t *testing.T) {
got, _ := dictionary.Search("test")
want := searchStr
assertStrings(t, got, want)
})
t.Run("unknown word", func(t *testing.T) {
_, err := dictionary.Search("not-test")
assertError(t, err, ErrNotFound)
// testing for an error's string is crude since error str can be modified
// see README.md pointers_errors directory
assertStrings(t, err.Error(), ErrNotFound.Error())
})
}
func TestAdd(t *testing.T) {
t.Run("new word", func(t *testing.T) {
dictionary := Dictionary{}
word := "test"
definition := "this is just a test"
dictionary.Add(word, definition)
assertDefinition(t, dictionary, word, definition)
})
t.Run("new word", func(t *testing.T) {
word := "test"
definition := "this is just a test"
dictionary := Dictionary{word: definition}
err := dictionary.Add(word, definition)
assertError(t, err, ErrWordExists)
assertDefinition(t, dictionary, word, definition)
})
}
func assertDefinition(t testing.TB, dictionary Dictionary, word, definition string) {
t.Helper()
got, err := dictionary.Search(word)
if err != nil {
t.Fatal("should find added word:", err)
}
assertStrings(t, got, definition)
}
func TestUpdate(t *testing.T) {
updateStr := "Don't build the Torment Nexus!"
t.Run("update works", func(t *testing.T) {
dictionary := Dictionary{"test": "this is just a test"}
dictionary.Update("test", updateStr)
assertDefinition(t, dictionary, "test", updateStr)
})
t.Run("key does not exist", func(t *testing.T) {
dictionary := Dictionary{}
err := dictionary.Update("test", updateStr)
assertError(t, err, ErrWordDoesNotExist)
})
}
func TestDelete(t *testing.T) {
dictionary := Dictionary{"test": "Don't build the Torment Nexus!"}
dictionary.Delete("test")
_, err := dictionary.Search("test")
assertError(t, err, ErrNotFound)
}

@ -0,0 +1,3 @@
module my_maps
go 1.21.0
Loading…
Cancel
Save