From 2f51b4a35a788d2f08a9177f5bcb1c2222f605cf Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 18 Oct 2023 23:17:54 +1100 Subject: [PATCH 1/3] Dev --- hashtable.go | 59 ++++++++++++++++++++++++++++++- hashtable_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/hashtable.go b/hashtable.go index e82cbc2..31f4989 100644 --- a/hashtable.go +++ b/hashtable.go @@ -92,6 +92,29 @@ func (hashtable *Hashtable[K, V]) AddMany(values ...map[K]V) *Hashtable[K, V] { return hashtable } +// AddManyOK inserts multiple key-value pairs into the hashtable and returns a slice of booleans indicating +// whether each insertion was successful. If a key already exists, it is not updated, and the corresponding +// boolean value is set to false in the returned slice. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// results := ht.AddManyOK( +// map[string]int{"apple": 5, "banana": 3}, +// map[string]int{"banana": 10, "cherry": 8}, +// ) +// // results contains [true, false, true] indicating successful insertions for "apple" and "cherry" +// // and unsuccessful insertion for "banana" due to existing key. +func (hashtable *Hashtable[K, V]) AddManyOK(values ...map[K]V) *slice.Slice[bool] { + successfulInsertions := make(slice.Slice[bool], 0) + for _, item := range values { + for key, value := range item { + successfulInsertions.Append(hashtable.AddOK(key, value)) + } + } + return &successfulInsertions +} + // AddOK inserts a new key-value pair into the hashtable if the key does not already exist. // It returns a boolean value indicating whether the key was added successfully (true) or if the key already existed (false). // @@ -179,6 +202,27 @@ func (hashtable *Hashtable[K, V]) DeleteMany(keys ...K) *Hashtable[K, V] { return hashtable } +// DeleteManyOK deletes multiple keys from the hashtable and returns a slice of booleans indicating whether each deletion was successful. +// For each specified key, it checks if the key exists in the hashtable before attempting deletion. If the key does not exist, +// the deletion is considered successful for that key, and true is appended to the returned slice. If the key exists and is successfully +// deleted, true is appended; otherwise, false is appended. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// keysToDelete := []string{"apple", "grape"} +// results := ht.DeleteManyOK(keysToDelete...) +// // results contains [true, true], indicating successful deletion of "apple" (exists) and "grape" (does not exist) +func (hashtable *Hashtable[K, V]) DeleteManyOK(keys ...K) *slice.Slice[bool] { + deletetions := make(slice.Slice[bool], 0) + for _, key := range keys { + deletetions.Append(hashtable.DeleteOK(key)) + } + return &deletetions +} + // DeleteManyValues deletes key-value pairs from the hashtable where the value matches any of the specified values. // // Example: @@ -197,13 +241,26 @@ func (hashtable *Hashtable[K, V]) DeleteManyValues(values ...V) *Hashtable[K, V] for _, v := range values { if reflect.DeepEqual(v, value) { hashtable.Delete(key) - break } } } return hashtable } +// DeleteOK deletes the specified key from the hashtable and returns a boolean indicating whether the deletion was successful. +// If the key does not exist in the hashtable, it is considered a successful deletion, and true is returned. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// deleted := ht.DeleteOK("apple") // true, "apple" key is successfully deleted +// notDeleted := ht.DeleteOK("grape") // true, "grape" key does not exist, deletion is considered successful +func (hashtable Hashtable[K, V]) DeleteOK(key K) bool { + return !hashtable.Delete(key).Has(key) +} + // Each iterates over the key-value pairs in the hashtable and applies a function to each pair. // // Example: diff --git a/hashtable_test.go b/hashtable_test.go index 9b3d57b..2dbe809 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -153,6 +153,42 @@ func TestAddMany(t *testing.T) { } } +// TestAddManyOK tests Hashtable.AddManyOK. +func TestAddManyOK(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + + // Attempt to add multiple key-value pairs and get the results indicating success. + results := ht.AddManyOK( + map[string]int{"apple": 5, "banana": 3, "cherry": 8}, + ) + + // Expected results: [true, true, true] indicating successful insertions for "apple", "banana" and "cherry". + expectedResults := []bool{true, true, true} + + // Verify that the obtained results match the expected results. + for i, result := range *results { + if result != expectedResults[i] { + t.Errorf("Expected result: %v, but got: %v", expectedResults[i], result) + } + } + + // Attempt to add multiple key-value pairs and get the results indicating success. + results = ht.AddManyOK( + map[string]int{"apple": 5, "banana": 3, "cherry": 8}, + ) + + // Expected results: [false, false, false] indicating unsuccessful insertions for "apple", "banana" and "cherry" due to existing key. + expectedResults = []bool{false, false, false} + + // Verify that the obtained results match the expected results. + for i, result := range *results { + if result != expectedResults[i] { + t.Errorf("Expected result: %v, but got: %v", expectedResults[i], result) + } + } +} + // TestAddOK tests Hashtable.AddOK. func TestAddOK(t *testing.T) { ht := make(hashtable.Hashtable[string, int]) @@ -313,6 +349,7 @@ func TestDeleteMany(t *testing.T) { } } +// TestDeleteManyValues tests Hashtable.DeleteManyValues. func TestDeleteManyValues(t *testing.T) { // Create a new hashtable. ht := make(hashtable.Hashtable[string, int]) @@ -330,6 +367,59 @@ func TestDeleteManyValues(t *testing.T) { } } +// TestDeleteManyOK tests Hashtable.DeleteManyOK. +func TestDeleteManyOK(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + + // Add key-value pairs to the hashtable. + ht.Add("apple", 5) + ht.Add("banana", 3) + + // Specify keys to delete. + keysToDelete := []string{"apple", "grape"} + + // Attempt to delete keys and check if deletion is successful. + results := ht.DeleteManyOK(keysToDelete...) + + expectedResults := []bool{true, true} // Expected results for "apple" (exists) and "grape" (does not exist) + + // Check if results match the expected results. + for i, result := range *results { + if result != expectedResults[i] { + t.Errorf("Expected deletion of key %s to be %v but got %v", keysToDelete[i], expectedResults[i], result) + } + } +} + +// TestDeleteOK tests Hashtable.DeleteOK. +func TestDeleteOK(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + + // Add key-value pairs to the hashtable. + ht.Add("apple", 5) + ht.Add("banana", 3) + + // Delete keys and check if deletion is successful. + deleted := ht.DeleteOK("apple") + if !deleted { + t.Errorf("Expected deletion of 'apple' to be successful") + } + + // Attempt to delete a key that does not exist. + notDeleted := ht.DeleteOK("grape") + if !notDeleted { + t.Errorf("Expected deletion of 'grape' to be successful because the key does not exist") + } + + // Attempt to delete a key that has already been deleted. + alreadyDeleted := ht.DeleteOK("apple") + if !alreadyDeleted { + t.Errorf("Expected deletion of 'apple' to be successful even though it was already deleted") + } +} + // TestEach tests Hashtable.Each. func TestEach(t *testing.T) { ht := make(hashtable.Hashtable[string, int]) From 6f1de916c4d3cf57a70c59bee61502a784e02b12 Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 18 Oct 2023 23:19:40 +1100 Subject: [PATCH 2/3] Dev --- hashtable.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashtable.go b/hashtable.go index 31f4989..8dde296 100644 --- a/hashtable.go +++ b/hashtable.go @@ -257,7 +257,7 @@ func (hashtable *Hashtable[K, V]) DeleteManyValues(values ...V) *Hashtable[K, V] // ht.Add("banana", 3) // deleted := ht.DeleteOK("apple") // true, "apple" key is successfully deleted // notDeleted := ht.DeleteOK("grape") // true, "grape" key does not exist, deletion is considered successful -func (hashtable Hashtable[K, V]) DeleteOK(key K) bool { +func (hashtable *Hashtable[K, V]) DeleteOK(key K) bool { return !hashtable.Delete(key).Has(key) } From 9bc4a45748a30e82a5524fdd3faf64d6c99f6bae Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Thu, 19 Oct 2023 11:54:35 +1100 Subject: [PATCH 3/3] Dev --- hashtable.go | 51 ++++++++++++++++++++------------ hashtable_test.go | 75 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 25 deletions(-) diff --git a/hashtable.go b/hashtable.go index 8dde296..81d7b47 100644 --- a/hashtable.go +++ b/hashtable.go @@ -204,7 +204,7 @@ func (hashtable *Hashtable[K, V]) DeleteMany(keys ...K) *Hashtable[K, V] { // DeleteManyOK deletes multiple keys from the hashtable and returns a slice of booleans indicating whether each deletion was successful. // For each specified key, it checks if the key exists in the hashtable before attempting deletion. If the key does not exist, -// the deletion is considered successful for that key, and true is appended to the returned slice. If the key exists and is successfully +// the deletion is considered unsuccessful for that key, and false is appended to the returned slice. If the key exists and is successfully // deleted, true is appended; otherwise, false is appended. // // Example: @@ -540,38 +540,51 @@ func (hashtable *Hashtable[K, V]) Length() int { return len(*hashtable) } -// Map applies a given function to all key-value pairs in the hashtable and returns a new hashtable with the transformed values. -// The original hashtable remains unchanged. +// Map iterates over the key-value pairs in the hashtable and applies the provided function to each pair. +// The function can modify the value. The modified key-value pairs are updated in the same hashtable. // // Example: // // ht := make(hashtable.Hashtable[string, int]) // ht.Add("apple", 5) // ht.Add("banana", 3) -// ht.Add("cherry", 8) -// -// // Define a function to double the values. -// doubleValue := func(key string, value int) int { -// return value * 2 -// } -// -// // Apply the function to double the values in the hashtable. -// doubledHT := ht.Map(doubleValue) -// // doubledHT contains: {"apple": 10, "banana": 6, "cherry": 16} +// ht.Map(func(key string, value int) int { +// if key == "banana" { +// return value * 2 // Modify the value for the "banana" key +// } +// return value // Leave other values unchanged +// }) +// // ht: {"apple": 5, "banana": 6} func (hashtable *Hashtable[K, V]) Map(fn func(key K, value V) V) *Hashtable[K, V] { - for key, value := range *hashtable { - hashtable.Add(key, fn(key, value)) - } - return hashtable + return hashtable.MapBreak(func(key K, value V) (V, bool) { + return fn(key, value), true + }) } +// MapBreak iterates over the key-value pairs in the hashtable and applies the provided function to each pair. +// The function can modify the value and return a boolean indicating whether to continue the iteration. +// If the function returns false, the iteration breaks, and a new hashtable with modified key-value pairs is returned. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// newHT := ht.MapBreak(func(key string, value int) (int, bool) { +// if key == "banana" { +// return value * 2, false // Break the iteration when key is "banana" +// } +// return value, true // Continue iterating for other keys +// }) +// // newHT: {"apple": 5} func (hashtable *Hashtable[K, V]) MapBreak(fn func(key K, value V) (V, bool)) *Hashtable[K, V] { + newHashtable := make(Hashtable[K, V]) for key, value := range *hashtable { value, ok := fn(key, value) if !ok { break } - hashtable.Add(key, value) + newHashtable.Add(key, value) } - return hashtable + return &newHashtable } diff --git a/hashtable_test.go b/hashtable_test.go index 2dbe809..b23c7c8 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -169,7 +169,7 @@ func TestAddManyOK(t *testing.T) { // Verify that the obtained results match the expected results. for i, result := range *results { if result != expectedResults[i] { - t.Errorf("Expected result: %v, but got: %v", expectedResults[i], result) + t.Fatalf("Expected result: %v, but got: %v", expectedResults[i], result) } } @@ -184,7 +184,7 @@ func TestAddManyOK(t *testing.T) { // Verify that the obtained results match the expected results. for i, result := range *results { if result != expectedResults[i] { - t.Errorf("Expected result: %v, but got: %v", expectedResults[i], result) + t.Fatalf("Expected result: %v, but got: %v", expectedResults[i], result) } } } @@ -387,7 +387,7 @@ func TestDeleteManyOK(t *testing.T) { // Check if results match the expected results. for i, result := range *results { if result != expectedResults[i] { - t.Errorf("Expected deletion of key %s to be %v but got %v", keysToDelete[i], expectedResults[i], result) + t.Fatalf("Expected deletion of key %s to be %v but got %v", keysToDelete[i], expectedResults[i], result) } } } @@ -404,19 +404,19 @@ func TestDeleteOK(t *testing.T) { // Delete keys and check if deletion is successful. deleted := ht.DeleteOK("apple") if !deleted { - t.Errorf("Expected deletion of 'apple' to be successful") + t.Fatalf("Expected deletion of 'apple' to be successful") } // Attempt to delete a key that does not exist. notDeleted := ht.DeleteOK("grape") if !notDeleted { - t.Errorf("Expected deletion of 'grape' to be successful because the key does not exist") + t.Fatalf("Expected deletion of 'grape' to be successful because the key does not exist") } // Attempt to delete a key that has already been deleted. alreadyDeleted := ht.DeleteOK("apple") if !alreadyDeleted { - t.Errorf("Expected deletion of 'apple' to be successful even though it was already deleted") + t.Fatalf("Expected deletion of 'apple' to be successful even though it was already deleted") } } @@ -729,6 +729,7 @@ func TestKeys(t *testing.T) { } } +// TestKeysFunc tests Hashtable.Keys. func TestKeysFunc(t *testing.T) { // Create a new hashtable. ht := make(hashtable.Hashtable[string, int]) @@ -750,6 +751,7 @@ func TestKeysFunc(t *testing.T) { } } +// TestLength tests Hashtable.Length. func TestLength(t *testing.T) { // Create a new hashtable. ht := make(hashtable.Hashtable[string, int]) @@ -768,3 +770,64 @@ func TestLength(t *testing.T) { t.Fatalf("Expected length: %d, but got: %d", expectedLength, length) } } + +func TestMap(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + + // Add key-value pairs to the hashtable. + ht["apple"] = 5 + ht["banana"] = 3 + ht["cherry"] = 8 + + // Define a function to double the values. + doubleValue := func(key string, value int) int { + return value * 2 + } + + // Apply the function to double the values in the hashtable. + doubledHT := ht.Map(doubleValue) + + // Expected doubled values. + expectedValues := map[string]int{"apple": 10, "banana": 6, "cherry": 16} + for key, expectedValue := range expectedValues { + value, exists := (*doubledHT)[key] + if !exists || value != expectedValue { + t.Fatalf("Expected value %d for key %s, but got %d", expectedValue, key, value) + } + } + + // Ensure the original hashtable remains unchanged. + for key, expectedValue := range expectedValues { + value, exists := ht[key] + if !exists || value != expectedValue/2 { + t.Fatalf("Expected original value %d for key %s, but got %d", expectedValue/2, key, value) + } + } +} + +// TestMapBreak tests Hashtable.MapBreak. +func TestMapBreak(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + + // Add key-value pairs to the hashtable. + ht["banana"] = 3 + + // Apply the MapBreak function to modify values and break the iteration at "banana". + ht.MapBreak(func(key string, value int) (int, bool) { + if key == "banana" { + return value * 2, false // Break the iteration when key is "banana" + } + return value * 2, true // Continue iterating for other keys and double the values + }) + + // Check if values are not modified as expected. + expectedValues := map[string]int{"banana": 3} + for key, expectedValue := range expectedValues { + value, exists := ht.Get(key) + if !exists || value != expectedValue { + t.Fatalf("Expected value %d for key %s, but got %d", expectedValue, key, value) + } + } +}