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