Finished maps
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…
Reference in New Issue