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