From 2f51b4a35a788d2f08a9177f5bcb1c2222f605cf Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 18 Oct 2023 23:17:54 +1100 Subject: [PATCH 1/6] 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/6] 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/6] 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) + } + } +} From 0fcbbd3557897d1f51604a67adccaa4b00dbbbaa Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 25 Oct 2023 14:05:36 +1100 Subject: [PATCH 4/6] Dev --- hashtable.go | 629 +++++++++++++++++++++++++++------------------- hashtable_test.go | 561 ++++++++++++++++++++++++++++++++--------- 2 files changed, 815 insertions(+), 375 deletions(-) diff --git a/hashtable.go b/hashtable.go index 81d7b47..2f15914 100644 --- a/hashtable.go +++ b/hashtable.go @@ -7,51 +7,32 @@ import ( ) // Hashtable represents a generic hash table that maps keys of type K to values of type V. It provides efficient key-value storage and retrieval operations. -// -// Example: -// -// // Create a new Hashtable with string keys and integer values. -// ht := make(hashtable.Hashtable[string, int]) -// -// // Insert key-value pairs into the hashtable. -// ht["apple"] = 5 -// ht["banana"] = 3 -// ht["cherry"] = 8 -// -// // Retrieve the value associated with a specific key. -// value := ht["banana"] // 3 -// -// // Check if a key exists in the hashtable. -// _, exists := ht["grape"] -// -// // Delete a key-value pair from the hashtable. -// delete(ht, "cherry") type Hashtable[K comparable, V any] map[K]V -// Add inserts a key-value pair into the hashtable. If the key already exists, its associated value is updated. -// -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) -// ht.Add("banana", 10) // Updates the value associated with the "banana" key. +// Add inserts a new key-value pair into the hashtable or updates the existing value associated with the provided key. +// If the key already exists, the corresponding value is updated. If the key is new, a new key-value pair is added to the hashtable. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) +// newHashtable.Add("banana", 10) // Updates the value for the key "banana" to 10 +// fmt.Println(newHashtable) // &map[apple:5 banana:10 cherry:8] func (hashtable *Hashtable[K, V]) Add(key K, value V) *Hashtable[K, V] { (*hashtable)[key] = value return hashtable } -// AddFunc inserts key-value pairs into the hashtable based on the provided maps and a custom validation function. -// The validation function should return true for key-value pairs that should be added to the hashtable, and false otherwise. +// AddFunc inserts key-value pairs into the hashtable based on the values provided in the input slice of maps. +// It applies the provided function to each key-value pair and adds them to the hashtable if the function returns true. +// If the key already exists in the hashtable, the corresponding value is updated with the new value from the input. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.AddFunc([]map[string]int{{"apple": 1, "orange": 2}}, func(key string, value int) bool { +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.AddFunc([]map[string]int{{"apple": 1, "orange": 2}}, func(key string, value int) bool { // // Only add key-value pairs where the value is greater than 1. // return value > 1 // }) +// fmt.Println(newHashtable) // &map[orange:10] func (hashtable *Hashtable[K, V]) AddFunc(values []map[K]V, fn func(key K, value V) bool) *Hashtable[K, V] { for _, item := range values { for key, value := range item { @@ -63,26 +44,25 @@ func (hashtable *Hashtable[K, V]) AddFunc(values []map[K]V, fn func(key K, value return hashtable } -// AddLength inserts a key-value pair into the hashtable and returns the new length of the hashtable. -// If the key already exists, its associated value is updated. -// -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// length := ht.AddLength("apple", 5) // length is 1 -// length = ht.AddLength("banana", 3) // length is 2 -// length = ht.AddLength("apple", 10) // length remains 2 (key "apple" is updated) +// AddLength inserts a new key-value pair into the hashtable or updates the existing value associated with the provided key. +// If the key already exists, the corresponding value is updated. If the key is new, a new key-value pair is added to the hashtable. +// It then returns the current length of the hashtable after the addition or update operation. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// length := newHashtable.AddLength("apple", 5) // Adds "apple" with value 5, returns the length of the hashtable (1 in this case) +// length = newHashtable.AddLength("apple", 10) // Updates the value for "apple" to 10, returns the length of the hashtable (1) +// length = newHashtable.AddLength("banana", 3) // Adds "banana" with value 3, returns the length of the hashtable (2) func (hashtable *Hashtable[K, V]) AddLength(key K, value V) int { return hashtable.Add(key, value).Length() } -// AddMany inserts multiple key-value pairs into the hashtable from the provided maps. -// If keys already exist, their associated values are updated. +// AddMany inserts multiple key-value pairs into the hashtable. It accepts a variadic number of maps, where each map contains +// key-value pairs to be added to the hashtable. If a key already exists in the hashtable, the corresponding value is updated +// with the new value from the input maps. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.AddMany(map[string]int{"orange": 7, "grape": 4}, map[string]int{"kiwi": 6, "pear": 9}) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.AddMany(map[string]int{"orange": 7, "grape": 4}, map[string]int{"kiwi": 6, "pear": 9}) +// fmt.Println(newHashtable) // &map[orange:7 grape:4 kiwi:6 pear:9] func (hashtable *Hashtable[K, V]) AddMany(values ...map[K]V) *Hashtable[K, V] { for _, item := range values { for key, value := range item { @@ -92,40 +72,58 @@ 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: +// AddManyFunc inserts key-value pairs into the hashtable based on a provided condition function. +// It accepts a slice of maps, where each map contains key-value pairs. For each key-value pair, +// the specified function is called. If the function returns true, the pair is added to the hashtable. // -// 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. +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.AddManyFunc([]map[K]V{{"apple": 5, "orange": -3, "banana": 10}}, func(i int, key string, value int) bool { +// return value > 0 // Add key-value pairs with values greater than 0 +// }) +// fmt.Println(newHashtable) // &map[apple:5 banana:10] +func (hashtable *Hashtable[K, V]) AddManyFunc(values []map[K]V, fn func(i int, key K, value V) bool) *Hashtable[K, V] { + for i, item := range values { + for key, value := range item { + if fn(i, key, value) { + hashtable.Add(key, value) + } + } + } + return hashtable +} + +// AddManyOK inserts multiple key-value pairs into the hashtable and returns a slice of booleans indicating whether each insertion was successful. +// It accepts a variadic number of maps, where each map contains key-value pairs to be added to the hashtable. +// For each key-value pair, it checks if the key already exists in the hashtable. If the key is not present, the pair is added, +// and the corresponding boolean in the returned slice is true. If the key already exists, the pair is not added, and the boolean is false. +// +// ht := make(Hashtable[string, int]) +// results := ht.AddManyOK(map[string]int{"apple": 5, "orange": 3}, map[string]int{"orange": 10, "banana": 7}) +// // Returns a slice containing [true, false, true] indicating successful insertions for "apple" and "banana" 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)) + ok := hashtable.Not(key) + if ok { + hashtable.Add(key, value) + } + successfulInsertions.Append(ok) } } 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). +// AddOK inserts a new key-value pair into the hashtable only if the key does not already exist in the hashtable. +// If the key already exists, the insertion fails, and false is returned. If the key is new, a new key-value pair is added to the hashtable, +// and true is returned to indicate a successful insertion. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) +// newHashtable := make(hashtable.Hashtable[string, int]) // // // Attempt to add key-value pairs. -// added := ht.AddOK("apple", 5) // added is true, "apple" is added with value 5. -// reAdded := ht.AddOK("apple", 10) // reAdded is false, "apple" already exists with value 5, no change is made. -// addedNew := ht.AddOK("banana", 3) // addedNew is true, "banana" is added with value 3. +// added := newHashtable.AddOK("apple", 5) // added is true, "apple" is added with value 5. +// reAdded := newHashtable.AddOK("apple", 10) // reAdded is false, "apple" already exists with value 5, no change is made. +// addedNew := newHashtable.AddOK("banana", 3) // addedNew is true, "banana" is added with value 3. func (hashtable *Hashtable[K, V]) AddOK(key K, value V) bool { ok := !hashtable.Has(key) if ok { @@ -134,34 +132,33 @@ func (hashtable *Hashtable[K, V]) AddOK(key K, value V) bool { return ok } -// Delete removes a key-value pair from the hashtable based on the provided key. +// Delete removes a key-value pair from the hashtable based on the provided key. If the key exists in the hashtable, +// it is deleted, and the modified hashtable is returned. If the key is not found, the hashtable remains unchanged. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Delete("apple") // Removes the key-value pair with key "apple" from the hashtable. +// // Delete the key-value pair with the key "apple". +// newHashtable.Delete("apple") +// fmt.Println(newHashtable) // &map[banana:3] func (hashtable *Hashtable[K, V]) Delete(key K) *Hashtable[K, V] { delete(*hashtable, key) return hashtable } -// DeleteFunc removes key-value pairs from the hashtable based on the evaluation performed by the provided function. -// For each key-value pair in the hashtable, the function fn is called with the key and value. -// If the function returns true, the key-value pair is removed from the hashtable. -// -// Example: +// DeleteFunc removes key-value pairs from the hashtable based on the provided function. The function is applied to each key-value pair, +// and if it returns true, the corresponding key-value pair is deleted from the hashtable. // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("orange", 7) -// ht.Add("kiwi", 6) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// // Delete key-value pairs where the value is less than 7. -// ht.DeleteFunc(func(key string, value int) bool { -// return value < 7 -// }) // Removes the key-value pair with key "kiwi" from the hashtable. +// // Delete key-value pairs where the value is less than 4. +// newHashtable.DeleteFunc(func(key string, value int) bool { +// return value < 4 +// }) +// fmt.Println(newHashtable) // &map[apple:5] func (hashtable *Hashtable[K, V]) DeleteFunc(fn func(key K, value V) bool) *Hashtable[K, V] { for key, value := range *hashtable { if fn(key, value) { @@ -171,30 +168,32 @@ func (hashtable *Hashtable[K, V]) DeleteFunc(fn func(key K, value V) bool) *Hash return hashtable } -// DeleteLength deletes a key from the hashtable and returns the new length of the hashtable. -// If the key does not exist, the length remains unchanged. -// -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// length := ht.DeleteLength("apple") // length is 1 (key "apple" is deleted) -// length = ht.DeleteLength("grape") // length remains 1 (key "grape" does not exist) +// DeleteLength removes a key-value pair from the hashtable based on the provided key. If the key exists in the hashtable, +// it is deleted, and the current length of the hashtable after the deletion is returned. If the key is not found, +// the hashtable remains unchanged, and the current length is returned. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// +// // Delete the key-value pair with the key "apple" and get the updated length of the hashtable. +// length := newHashtable.DeleteLength("apple") +// // After deletion, the length of the hashtable is 1. +// // The current length returned: 1 func (hashtable *Hashtable[K, V]) DeleteLength(key K) int { return hashtable.Delete(key).Length() } -// DeleteMany removes multiple key-value pairs from the hashtable based on the provided keys. -// If a key doesn't exist in the hashtable, it is ignored. +// DeleteMany removes multiple key-value pairs from the hashtable based on the provided keys. If a key exists in the hashtable, +// it is deleted. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("orange", 7) -// ht.Add("kiwi", 6) -// ht.DeleteMany("apple", "kiwi") // Removes key-value pairs with keys "apple" and "kiwi" from the hashtable. +// // Delete key-value pairs with the keys "apple" and "banana". +// newHashtable.DeleteMany("apple", "banana") +// fmt.Println(newHashtable) // &map[] func (hashtable *Hashtable[K, V]) DeleteMany(keys ...K) *Hashtable[K, V] { for _, key := range keys { hashtable.Delete(key) @@ -202,19 +201,17 @@ 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 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. +// DeleteManyOK removes multiple key-value pairs from the hashtable based on the provided keys. If a key exists in the hashtable, +// it is deleted, and true is appended to the result slice to indicate a successful deletion. If the key is not found, false is appended. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// 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) +// // Attempt to delete key-value pairs with the keys "apple" and "orange". +// results := newHashtable.DeleteManyOK("apple", "orange") +// // Results after deletion: []bool{true, false} +// // The first deletion succeeded ("apple": 5 was deleted), and the second deletion failed as "orange" was not found. func (hashtable *Hashtable[K, V]) DeleteManyOK(keys ...K) *slice.Slice[bool] { deletetions := make(slice.Slice[bool], 0) for _, key := range keys { @@ -223,19 +220,16 @@ func (hashtable *Hashtable[K, V]) DeleteManyOK(keys ...K) *slice.Slice[bool] { return &deletetions } -// DeleteManyValues deletes key-value pairs from the hashtable where the value matches any of the specified values. -// -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// DeleteManyValues removes key-value pairs from the hashtable based on the provided values. If a value exists in the hashtable, +// the corresponding key-value pair is deleted. // -// // Delete key-value pairs where the value is 3 or 8. -// ht.DeleteManyValues(3, 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// // The hashtable after deletion: {"apple": 5} +// // Delete key-value pairs with the values 5 and 10. +// newHashtable.DeleteManyValues(5, 10) +// // Hashtable after deletion: {"banana": 3} func (hashtable *Hashtable[K, V]) DeleteManyValues(values ...V) *Hashtable[K, V] { for key, value := range *hashtable { for _, v := range values { @@ -247,28 +241,33 @@ func (hashtable *Hashtable[K, V]) DeleteManyValues(values ...V) *Hashtable[K, V] 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 +// DeleteOK removes a key-value pair from the hashtable based on the provided key. If the key exists in the hashtable, +// it is deleted, and true is returned to indicate a successful deletion. If the key is not found, the hashtable remains unchanged, +// and false is returned to indicate that the deletion failed. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// +// // Attempt to delete the key-value pair with the key "apple". +// success := newHashtable.DeleteOK("apple") +// // After deletion, the key "apple" is not found in the hashtable. +// // Deletion success: true +// +// // Attempt to delete a non-existing key. +// success = newHashtable.DeleteOK("orange") +// // The key "orange" does not exist in the hashtable. +// // Deletion failed: false 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: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to print all key-value pairs. // printKeyValue := func(key string, value int) { @@ -276,7 +275,7 @@ func (hashtable *Hashtable[K, V]) DeleteOK(key K) bool { // } // // // Iterate over the hashtable and print all key-value pairs. -// ht.Each(printKeyValue) +// newHashtable.Each(printKeyValue) // // Output: "apple 5", "banana 3", "cherry 8" func (hashtable *Hashtable[K, V]) Each(fn func(key K, value V)) *Hashtable[K, V] { return hashtable.EachBreak(func(key K, value V) bool { @@ -285,18 +284,17 @@ func (hashtable *Hashtable[K, V]) Each(fn func(key K, value V)) *Hashtable[K, V] }) } -// EachBreak iterates over the key-value pairs in the hashtable and applies a function to each pair. -// If the function returns false at any point, the iteration breaks. +// EachBreak applies the provided function to each key-value pair in the hashtable. The function is applied to key-value pairs +// in the hashtable until the provided function returns false. If the function returns false for any key-value pair, +// the iteration breaks early. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to print key-value pairs until finding "banana". -// stopPrinting := ht.EachBreak(func(key string, value int) bool { +// stopPrinting := newHashtable.EachBreak(func(key string, value int) bool { // fmt.Println(key, value) // return key != "banana" // Continue printing until "banana" is encountered. // }) @@ -312,12 +310,10 @@ func (hashtable *Hashtable[K, V]) EachBreak(fn func(key K, value V) bool) *Hasht // EachKey iterates over the keys in the hashtable and applies a function to each key. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to print each key. // printKey := func(key string) { @@ -325,7 +321,7 @@ func (hashtable *Hashtable[K, V]) EachBreak(fn func(key K, value V) bool) *Hasht // } // // // Iterate over the hashtable and print each key. -// ht.EachKey(printKey) +// newHashtable.EachKey(printKey) // // Output: "apple", "banana", "cherry" func (hashtable *Hashtable[K, V]) EachKey(fn func(key K)) *Hashtable[K, V] { return hashtable.Each(func(key K, _ V) { @@ -335,12 +331,10 @@ func (hashtable *Hashtable[K, V]) EachKey(fn func(key K)) *Hashtable[K, V] { // EachKeyBreak iterates over the keys in the hashtable and applies a function to each key. It allows breaking the iteration early if the provided function returns false. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to print each key and break the iteration if the key is "banana". // printAndBreak := func(key string) bool { @@ -349,7 +343,7 @@ func (hashtable *Hashtable[K, V]) EachKey(fn func(key K)) *Hashtable[K, V] { // } // // // Iterate over the hashtable keys, print them, and break when "banana" is encountered. -// ht.EachKeyBreak(printAndBreak) +// newHashtable.EachKeyBreak(printAndBreak) // // Output: "apple", "banana" func (hashtable *Hashtable[K, V]) EachKeyBreak(fn func(key K) bool) *Hashtable[K, V] { return hashtable.EachBreak(func(key K, _ V) bool { @@ -359,12 +353,10 @@ func (hashtable *Hashtable[K, V]) EachKeyBreak(fn func(key K) bool) *Hashtable[K // EachValue iterates over the values in the hashtable and applies a function to each value. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to print each value. // printValue := func(value int) { @@ -372,7 +364,7 @@ func (hashtable *Hashtable[K, V]) EachKeyBreak(fn func(key K) bool) *Hashtable[K // } // // // Iterate over the hashtable values and print them. -// ht.EachValue(printValue) +// newHashtable.EachValue(printValue) // // Output: 5, 3, 8 func (hashtable *Hashtable[K, V]) EachValue(fn func(value V)) *Hashtable[K, V] { return hashtable.Each(func(_ K, value V) { @@ -383,12 +375,10 @@ func (hashtable *Hashtable[K, V]) EachValue(fn func(value V)) *Hashtable[K, V] { // EachValueBreak iterates over the values in the hashtable and applies a function to each value until the function returns false. // If the provided function returns false, the iteration breaks early. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Function to process each value. Returns false to break the iteration if the value is 3. // processValue := func(value int) bool { @@ -397,7 +387,7 @@ func (hashtable *Hashtable[K, V]) EachValue(fn func(value V)) *Hashtable[K, V] { // } // // // Iterate over the hashtable values and process them until the value is 3. -// ht.EachValueBreak(processValue) +// newHashtable.EachValueBreak(processValue) // // Output: 5, 3 func (hashtable *Hashtable[K, V]) EachValueBreak(fn func(value V) bool) *Hashtable[K, V] { return hashtable.EachBreak(func(_ K, value V) bool { @@ -408,29 +398,51 @@ func (hashtable *Hashtable[K, V]) EachValueBreak(fn func(value V) bool) *Hashtab // Get retrieves the value associated with the provided key from the hashtable. // If the key exists, it returns the associated value and true. Otherwise, it returns the zero value for the value type and false. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// value, exists := ht.Get("apple") // 5, true -// value, exists = ht.Get("orange") // 0, false +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// value, exists := newHashtable.Get("apple") // 5, true +// value, exists = newHashtable.Get("orange") // 0, false func (hashtable *Hashtable[K, V]) Get(key K) (V, bool) { value, ok := (*hashtable)[key] return value, ok } -// GetMany retrieves values from the hashtable for the specified keys and returns them as a slice. -// If a key is not found in the hashtable, it is skipped in the result slice. +// Filter applies the given function to each key-value pair in the hashtable and returns a new hashtable +// containing only the key-value pairs for which the function returns true. The original hashtable is not modified. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// // Function to filter key-value pairs. Returns true if the value is greater than 4. +// filterFunc := func(key string, value int) bool { +// return value > 4 +// } +// +// // Create a new hashtable containing key-value pairs where the value is greater than 4. +// filteredHashtable := newHashtable.Filter(filterFunc) +func (hashtable *Hashtable[K, V]) Filter(fn func(key K, value V) bool) *Hashtable[K, V] { + filteredHashtable := make(Hashtable[K, V], 0) + hashtable.Each(func(key K, value V) { + if fn(key, value) { + filteredHashtable.Add(key, value) + } + }) + return &filteredHashtable +} + +// GetMany retrieves the values associated with the provided keys from the hashtable. It accepts a variadic number of keys, +// and returns a slice containing the values corresponding to the keys found in the hashtable. If a key is not found in the hashtable, +// the corresponding position in the returned slice will be the zero value for the value type. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Get values for specific keys. -// values := ht.GetMany("apple", "banana", "orange") +// values := newHashtable.GetMany("apple", "banana", "orange") // // // The resulting values slice: {5, 3} func (hashtable *Hashtable[K, V]) GetMany(keys ...K) *slice.Slice[V] { @@ -446,12 +458,10 @@ func (hashtable *Hashtable[K, V]) GetMany(keys ...K) *slice.Slice[V] { // Has checks if the provided key exists in the hashtable. // It returns true if the key exists, and false otherwise. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// exists := ht.Has("apple") // true -// exists = ht.Has("orange") // false +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// exists := newHashtable.Has("apple") // true +// exists = newHashtable.Has("orange") // false func (hashtable *Hashtable[K, V]) Has(key K) bool { _, ok := (*hashtable)[key] return ok @@ -460,16 +470,14 @@ func (hashtable *Hashtable[K, V]) Has(key K) bool { // HasMany checks the existence of multiple keys in the hashtable and returns a slice of boolean values // indicating whether each corresponding key exists in the hashtable. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Check the existence of multiple keys. // keysToCheck := []string{"apple", "orange", "banana"} -// results := ht.HasMany(keysToCheck...) +// results := newHashtable.HasMany(keysToCheck...) // // // The resulting boolean slice: {true, false, true} func (hashtable *Hashtable[K, V]) HasMany(keys ...K) *slice.Slice[bool] { @@ -484,15 +492,13 @@ func (hashtable *Hashtable[K, V]) HasMany(keys ...K) *slice.Slice[bool] { // Keys returns a slice containing all the keys present in the hashtable. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Get all keys from the hashtable. -// keys := ht.Keys() // Result: {"apple", "banana", "cherry"} +// keys := newHashtable.Keys() // Result: {"apple", "banana", "cherry"} func (hashtable *Hashtable[K, V]) Keys() *slice.Slice[K] { keys := make(slice.Slice[K], 0) hashtable.EachKey(func(key K) { @@ -501,18 +507,15 @@ func (hashtable *Hashtable[K, V]) Keys() *slice.Slice[K] { return &keys } -// KeysFunc returns a slice containing the keys from the hashtable for which the provided function returns true. -// The provided function `fn` should accept a key of type `K` and return a boolean value. +// KeysFunc applies the provided function to each key in the hashtable and returns a slice containing the keys for which the function returns true. // -// Example: -// -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // // // Get keys from the hashtable where the key length is greater than 5. -// keys := ht.KeysFunc(func(key string) bool { +// keys := newHashtable.KeysFunc(func(key string) bool { // return len(key) > 5 // }) // // Result: {"banana"} @@ -528,55 +531,51 @@ func (hashtable *Hashtable[K, V]) KeysFunc(fn func(key K) bool) *slice.Slice[K] // Length returns the number of key-value pairs in the hashtable. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// ht.Add("cherry", 8) -// -// length := ht.Length() // Result: 3 +// length := newHashtable.Length() // Result: 3 func (hashtable *Hashtable[K, V]) Length() int { return len(*hashtable) } -// 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. +// Map applies the provided function to each key-value pair in the hashtable and returns a new hashtable containing the mapped key-value pairs. +// The original hashtable remains unchanged. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// 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} +// // Function to double the values of each key-value pair. +// doubleValues := func(key string, value int) int { +// return value * 2 +// } +// +// // Create a new hashtable with doubled values. +// newHashtable := newHashtable.Map(doubleValues) +// // New hashtable: {"apple": 10, "banana": 6} func (hashtable *Hashtable[K, V]) Map(fn func(key K, value V) V) *Hashtable[K, V] { 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 +// MapBreak applies the provided function to each key-value pair in the hashtable. It creates a new hashtable containing the mapped key-value pairs +// until the function returns false for any pair, at which point the mapping breaks early. The original hashtable remains unchanged. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) +// +// // Create a new hashtable with doubled values until a value is greater than or equal to 10. +// newHashtable := newHashtable.MapBreak(func(key string, value int) (int, bool) { +// newValue := value * 2 +// return newValue, newValue < 10 // }) -// // newHT: {"apple": 5} +// // New hashtable: {"apple": 10, "banana": 6} 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 { @@ -588,3 +587,111 @@ func (hashtable *Hashtable[K, V]) MapBreak(fn func(key K, value V) (V, bool)) *H } return &newHashtable } + +// Not checks if the given key is not present in the hashtable. +// It returns true if the key is not found, and false if the key exists in the hashtable. +// +// ht := make(Hashtable[string, int]) +// result := ht.Not("apple") // Returns true if "apple" is not in the hashtable, false otherwise +func (hashtable *Hashtable[K, V]) Not(key K) bool { + return !hashtable.Has(key) +} + +// Pop removes a key-value pair from the hashtable based on the provided key. +// It returns the removed value and a boolean indicating whether the key was found and removed successfully. +// If the key is present in the hashtable, the corresponding value is returned, and the key-value pair is deleted. +// If the key is not found, it returns the zero value for the value type and false. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// removedValue, ok := ht.Pop("apple") // Removes the key "apple" and returns its associated value 5, ok is true +// removedValue, ok = ht.Pop("banana") // Key "banana" not found, removedValue is 0 and ok is false +func (hashtable *Hashtable[K, V]) Pop(key K) (V, bool) { + value, ok := hashtable.Get(key) + if ok { + ok = hashtable.DeleteOK(key) + } + return value, ok +} + +// PopMany removes multiple key-value pairs from the hashtable based on the provided keys. +// It takes a variadic number of keys as input and removes the corresponding key-value pairs from the hashtable. +// It returns a slice containing the removed values and does not guarantee any specific order of values in the result. +// If a key is not found in the hashtable, the corresponding value in the result slice will be the zero value for the value type. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// removedValues := ht.PopMany("apple", "orange") // Removes "apple", returns a slice containing [5, 0] +func (hashtable *Hashtable[K, V]) PopMany(keys ...K) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + for _, key := range keys { + value, ok := hashtable.Pop(key) + if ok { + values.Append(value) + } + } + return &values +} + +// Update 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 the update should be performed. +// If the function returns true, the key-value pair is updated in the same hashtable with the modified value. +// If the function returns false, the key-value pair is not modified. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Update(func(key string, value int) (int, bool) { +// if key == "banana" { +// return value * 2, true // Modify the value for the "banana" key +// } +// return value, false // Leave other values unchanged +// }) +// // newHashtable: {"apple": 5, "banana": 6} +func (hashtable *Hashtable[K, V]) Update(fn func(key K, value V) (V, bool)) *Hashtable[K, V] { + for key, value := range *hashtable { + if updatedValue, ok := fn(key, value); ok { + hashtable.Add(key, updatedValue) + } + } + return hashtable +} + +// Values returns a slice containing all the values present in the hashtable. +// It iterates over the hashtable and collects all the values in the order of insertion. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("orange", 10) +// values := newHashtable.Values() // Returns a slice containing [5, 10] +func (hashtable *Hashtable[K, V]) Values() *slice.Slice[V] { + i := 0 + values := make(slice.Slice[V], hashtable.Length()) + hashtable.EachValue(func(value V) { + values.Replace(i, value) + i++ + }) + return &values +} + +// ValuesFunc returns a slice containing values from the hashtable that satisfy the given condition function. +// The condition function takes a key-value pair as input and returns true if the pair meets the condition, false otherwise. +// It iterates over the hashtable and includes the values in the returned slice for which the condition function evaluates to true. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("orange", 10) +// filterFunc := func(key string, value int) bool { +// return value > 7 // Include values greater than 7 in the result +// } +// values := ht.ValuesFunc(filterFunc) // Returns a slice containing [10] +func (hashtable *Hashtable[K, V]) ValuesFunc(fn func(key K, value V) bool) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + hashtable.Each(func(key K, value V) { + if fn(key, value) { + values.Append(value) + } + }) + return &values +} diff --git a/hashtable_test.go b/hashtable_test.go index b23c7c8..c829169 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -19,36 +19,39 @@ func TestHashtable(t *testing.T) { // TestAdd tests Hashtable.Add. func TestAdd(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - - // Add key-value pairs to the hashtable. + // Test case 1: Add key-value pairs to an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. ht.Add("apple", 5) ht.Add("banana", 3) - ht.Add("cherry", 8) - // Verify that the key-value pairs have been added correctly. - expected := map[string]int{ - "apple": 5, - "banana": 3, - "cherry": 8, + // Verify the values using Get method. + value, ok := ht.Get("apple") + if !ok || value != 5 { + t.Errorf("Expected key 'apple' to have value 5, but got %v", value) + } + value, ok = ht.Get("banana") + if !ok || value != 3 { + t.Errorf("Expected key 'banana' to have value 3, but got %v", value) } - for key, expectedValue := range expected { - actualValue, ok := ht[key] - if !ok { - t.Fatalf("Key %s not found in the hashtable", key) - } else if actualValue != expectedValue { - t.Fatalf("Expected value for key %s is %d, but got %d", key, expectedValue, actualValue) - } + // Test case 2: Update existing key-value pair. + ht.Add("banana", 10) // Updates the value for the key "banana" to 10. + value, ok = ht.Get("banana") + if !ok || value != 10 { + t.Errorf("Expected key 'banana' to have updated value 10, but got %v", value) } - // Update the value associated with the "banana" key. - ht.Add("banana", 10) + // Test case 3: Add a new key-value pair. + ht.Add("cherry", 8) + value, ok = ht.Get("cherry") + if !ok || value != 8 { + t.Errorf("Expected key 'cherry' to have value 8, but got %v", value) + } - // Verify that the value has been updated correctly. - if ht["banana"] != 10 { - t.Fatalf("Expected value for key 'banana' to be updated to 10, but got %d", ht["banana"]) + // Verify that other keys are not affected. + value, ok = ht.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be absent, but it was found with value %v", value) } } @@ -87,104 +90,172 @@ func TestAddFunc(t *testing.T) { // TestAddLength tests Hashtable.AddLength. func TestAddLength(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - - // Add key-value pairs to the hashtable and get the length. + // Test case 1: Add key-value pairs to an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. length := ht.AddLength("apple", 5) + if length != 1 { + t.Errorf("Expected length of hashtable after adding 'apple' with value 5 to be 1, but got %v", length) + } - // Expected length after adding the first key-value pair: 1 - expectedLength := 1 - - // Verify that the obtained length matches the expected length. - if length != expectedLength { - t.Fatalf("Expected length: %d, but got: %d", expectedLength, length) + // Test case 2: Update an existing key-value pair. + length = ht.AddLength("apple", 10) + if length != 1 { + t.Errorf("Expected length of hashtable after updating 'apple' with value 10 to be 1, but got %v", length) } - // Add another key-value pair and get the updated length. + // Test case 3: Add a new key-value pair. length = ht.AddLength("banana", 3) - - // Expected length after adding the second key-value pair: 2 - expectedLength = 2 - - // Verify that the obtained length matches the expected length. - if length != expectedLength { - t.Fatalf("Expected length: %d, but got: %d", expectedLength, length) + if length != 2 { + t.Errorf("Expected length of hashtable after adding 'banana' with value 3 to be 2, but got %v", length) } - // Update an existing key-value pair and get the same length. - length = ht.AddLength("apple", 10) - - // Length should remain 2 after updating the existing key "apple". - // Expected length: 2 - expectedLength = 2 + // Test case 4: Add another new key-value pair. + length = ht.AddLength("cherry", 8) + if length != 3 { + t.Errorf("Expected length of hashtable after adding 'cherry' with value 8 to be 3, but got %v", length) + } - // Verify that the obtained length matches the expected length. - if length != expectedLength { - t.Fatalf("Expected length: %d, but got: %d", expectedLength, length) + // Verify that other keys are not affected. + value, ok := ht.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be absent, but it was found with value %v", value) } } // TestAddMany tests Hashtable.AddMany. func TestAddMany(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) + // Test case 1: Add key-value pairs to an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.AddMany(map[string]int{"orange": 7, "grape": 4}, map[string]int{"kiwi": 6, "pear": 9}) - // Test case 1. - ht.AddMany(map[string]int{"orange": 7, "grape": 4}) - expected1 := 7 - if val, ok := ht["orange"]; !ok || val != expected1 { - t.Fatalf("Expected %d, but got %d for key 'orange'", expected1, val) + // Verify the added key-value pairs. + expected := map[string]int{"orange": 7, "grape": 4, "kiwi": 6, "pear": 9} + for key, expectedValue := range expected { + value, ok := ht.Get(key) + if !ok { + t.Errorf("Expected key '%s' to be present, but it was not found", key) + } + if value != expectedValue { + t.Errorf("Expected value for key '%s' to be %d, but got %d", key, expectedValue, value) + } } - expected2 := 4 - if val, ok := ht["grape"]; !ok || val != expected2 { - t.Fatalf("Expected %d, but got %d for key 'grape'", expected2, val) + + // Test case 2: Update existing key-value pairs and add new ones. + ht.AddMany(map[string]int{"orange": 10, "grape": 3}, map[string]int{"apple": 5, "banana": 8}) + + // Verify the updated and added key-value pairs. + expected = map[string]int{"orange": 10, "grape": 3, "kiwi": 6, "pear": 9, "apple": 5, "banana": 8} + for key, expectedValue := range expected { + value, ok := ht.Get(key) + if !ok { + t.Errorf("Expected key '%s' to be present, but it was not found", key) + } + if value != expectedValue { + t.Errorf("Expected value for key '%s' to be %d, but got %d", key, expectedValue, value) + } } - // Test case 2. - ht.AddMany(map[string]int{"kiwi": 6, "pear": 9}) - expected3 := 6 - if val, ok := ht["kiwi"]; !ok || val != expected3 { - t.Fatalf("Expected %d, but got %d for key 'kiwi'", expected3, val) + // Test case 3: Add new key-value pairs. + ht.AddMany(map[string]int{"watermelon": 12, "cherry": 7}) + + // Verify the added key-value pairs. + expected = map[string]int{"orange": 10, "grape": 3, "kiwi": 6, "pear": 9, "apple": 5, "banana": 8, "watermelon": 12, "cherry": 7} + for key, expectedValue := range expected { + value, ok := ht.Get(key) + if !ok { + t.Errorf("Expected key '%s' to be present, but it was not found", key) + } + if value != expectedValue { + t.Errorf("Expected value for key '%s' to be %d, but got %d", key, expectedValue, value) + } } - expected4 := 9 - if val, ok := ht["pear"]; !ok || val != expected4 { - t.Fatalf("Expected %d, but got %d for key 'pear'", expected4, val) + + // Verify that other keys are not affected. + _, ok := ht.Get("pineapple") + if ok { + t.Errorf("Expected key 'pineapple' to be absent, but it was found") + } +} + +// TestAddManyFunc tests Hashtable.AddManyFunc. +func TestAddManyFunc(t *testing.T) { + // Test case 1: Add key-value pairs with values greater than 0 to the hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + values := []map[string]int{{"apple": 5, "orange": -3, "banana": 10}} + condition := func(i int, key string, value int) bool { + return value > 0 + } + ht.AddManyFunc(values, condition) + + // Verify that only the key-value pairs with values greater than 0 are added to the hashtable. + expected := &hashtable.Hashtable[string, int]{"apple": 5, "banana": 10} + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } + + // Test case 2: Add all key-value pairs to the hashtable. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + values = []map[string]int{{"apple": 5, "orange": -3, "banana": 10}} + condition = func(i int, key string, value int) bool { + return true // Add all key-value pairs + } + ht.AddManyFunc(values, condition) + + // Verify that all key-value pairs are added to the hashtable. + expected = &hashtable.Hashtable[string, int]{"apple": 5, "orange": -3, "banana": 10} + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) } } // 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. + // Test case 1: Add key-value pairs to an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. results := ht.AddManyOK( - map[string]int{"apple": 5, "banana": 3, "cherry": 8}, + map[string]int{"apple": 5, "banana": 3}, + map[string]int{"banana": 10, "cherry": 8}, ) - // Expected results: [true, true, true] indicating successful insertions for "apple", "banana" and "cherry". - expectedResults := []bool{true, true, true} + // Verify the success status of insertions. + expectedResults := &slice.Slice[bool]{true, true, false, true} + if !reflect.DeepEqual(results, expectedResults) { + t.Errorf("Expected insertion results %v, but got %v", expectedResults, *results) + } - // Verify that the obtained results match the expected results. - for i, result := range *results { - if result != expectedResults[i] { - t.Fatalf("Expected result: %v, but got: %v", expectedResults[i], result) + // Verify the added and updated key-value pairs. + expected := map[string]int{"apple": 5, "cherry": 8} + for key, expectedValue := range expected { + value, ok := ht.Get(key) + if !ok { + t.Errorf("Expected key '%s' to be present, but it was not found", key) + } + if value != expectedValue { + t.Errorf("Expected value for key '%s' to be %d, but got %d", key, expectedValue, value) } } - // Attempt to add multiple key-value pairs and get the results indicating success. + // Test case 2: Add new key-value pairs. results = ht.AddManyOK( - map[string]int{"apple": 5, "banana": 3, "cherry": 8}, + map[string]int{"watermelon": 12, "pineapple": 7}, ) - // Expected results: [false, false, false] indicating unsuccessful insertions for "apple", "banana" and "cherry" due to existing key. - expectedResults = []bool{false, false, false} + // Verify the success status of insertions. + expectedResults = &slice.Slice[bool]{true, true} + if !reflect.DeepEqual(results, expectedResults) { + t.Errorf("Expected insertion results %v, but got %v", expectedResults, *results) + } - // Verify that the obtained results match the expected results. - for i, result := range *results { - if result != expectedResults[i] { - t.Fatalf("Expected result: %v, but got: %v", expectedResults[i], result) + // Verify the added key-value pairs. + expected = map[string]int{"apple": 5, "cherry": 8, "watermelon": 12, "pineapple": 7} + for key, expectedValue := range expected { + value, ok := ht.Get(key) + if !ok { + t.Errorf("Expected key '%s' to be present, but it was not found", key) + } + if value != expectedValue { + t.Errorf("Expected value for key '%s' to be %d, but got %d", key, expectedValue, value) } } } @@ -299,7 +370,7 @@ func TestDeleteLength(t *testing.T) { // Delete an existing key from the hashtable and get the updated length. lengthAfterDelete := ht.DeleteLength("apple") - // Expected length after deleting "apple": initial length - 1 + // Expected length after deleting "apple": initial length - 1. expectedLength := initialLength - 1 // Verify that the obtained length matches the expected length. @@ -311,7 +382,7 @@ func TestDeleteLength(t *testing.T) { lengthAfterNonExistingDelete := ht.DeleteLength("grape") // Length should remain the same after attempting to delete a non-existing key. - // Expected length: initial length + // Expected length: initial length. expectedLengthNonExisting := len(ht) // Verify that the obtained length matches the expected length. @@ -349,24 +420,6 @@ func TestDeleteMany(t *testing.T) { } } -// TestDeleteManyValues tests Hashtable.DeleteManyValues. -func TestDeleteManyValues(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - ht.Add("apple", 5) - ht.Add("banana", 3) - ht.Add("cherry", 8) - - // Delete key-value pairs where the value is 3 or 8. - ht.DeleteManyValues(3, 8) - - // Verify that the hashtable only contains the expected key-value pair. - expected := hashtable.Hashtable[string, int]{"apple": 5} - if !reflect.DeepEqual(ht, expected) { - t.Fatalf("Expected hashtable: %v, but got: %v", expected, ht) - } -} - // TestDeleteManyOK tests Hashtable.DeleteManyOK. func TestDeleteManyOK(t *testing.T) { // Create a new hashtable. @@ -382,7 +435,7 @@ func TestDeleteManyOK(t *testing.T) { // 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) + 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 { @@ -392,6 +445,24 @@ func TestDeleteManyOK(t *testing.T) { } } +// TestDeleteManyValues tests Hashtable.DeleteManyValues. +func TestDeleteManyValues(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + ht.Add("apple", 5) + ht.Add("banana", 3) + ht.Add("cherry", 8) + + // Delete key-value pairs where the value is 3 or 8. + ht.DeleteManyValues(3, 8) + + // Verify that the hashtable only contains the expected key-value pair. + expected := hashtable.Hashtable[string, int]{"apple": 5} + if !reflect.DeepEqual(ht, expected) { + t.Fatalf("Expected hashtable: %v, but got: %v", expected, ht) + } +} + // TestDeleteOK tests Hashtable.DeleteOK. func TestDeleteOK(t *testing.T) { // Create a new hashtable. @@ -501,7 +572,7 @@ func TestEachKey(t *testing.T) { // Sort the printed values for consistent comparison. sort.Strings(printedKeys) - // Expected output: "apple", "banana", "cherry" + // Expected output: "apple", "banana", "cherry". expectedKeys := []string{"apple", "banana", "cherry"} for i, key := range printedKeys { if key != expectedKeys[i] { @@ -552,7 +623,7 @@ func TestEachValue(t *testing.T) { // Sort the printed values for consistent comparison. sort.Ints(printedValues) - // Expected output: 3, 5, 8 + // Expected output: 3, 5, 8. expectedValues := []int{3, 5, 8} if len(printedValues) != len(expectedValues) { @@ -610,6 +681,47 @@ func TestEachValueBreak(t *testing.T) { } } +// TestFilter tests Hashtable.Filter. +func TestFilter(t *testing.T) { + // Test case 1: Filter with an empty hashtable and a function that never selects any pairs. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + filterFunc := func(key string, value int) bool { + return false // Never select any values + } + filtered := ht.Filter(filterFunc) + expected := &hashtable.Hashtable[string, int]{} // Expected empty hashtable. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } + + // Test case 2: Filter with a non-empty hashtable and a function that never selects any pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + filterFunc = func(key string, value int) bool { + return false // Never select any values + } + filtered = ht.Filter(filterFunc) + expected = &hashtable.Hashtable[string, int]{} // Expected empty hashtable. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } + + // Test case 3: Filter with a non-empty hashtable and a function that selects certain pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + ht.Add("banana", 3) + filterFunc = func(key string, value int) bool { + return value > 4 // Select pairs where value is greater than 4 + } + filtered = ht.Filter(filterFunc) + expected = &hashtable.Hashtable[string, int]{"apple": 5, "orange": 10} // Expected filtered hashtable. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } +} + // TestGet tests Hashtable.Get. func TestGet(t *testing.T) { @@ -694,7 +806,7 @@ func TestHasMany(t *testing.T) { // Check the existence of multiple keys. results := ht.HasMany(keysToCheck...) - // The expected boolean slice: {true, false, true} + // The expected boolean slice: {true, false, true}. expectedResults := &slice.Slice[bool]{true, false, true} // Verify that the obtained results match the expected results. @@ -717,7 +829,7 @@ func TestKeys(t *testing.T) { // Sort the keys for consistent iteration order. sort.Strings(*keys) - // The expected keys slice: {"apple", "banana", "cherry"} + // The expected keys slice: {"apple", "banana", "cherry"}. expectedKeys := &slice.Slice[string]{"apple", "banana", "cherry"} // Sort the keys for consistent iteration order. @@ -742,7 +854,7 @@ func TestKeysFunc(t *testing.T) { return strings.HasPrefix(key, "b") }) - // The expected keys slice: {"banana"} + // The expected keys slice: {"banana"}. expectedKeys := &slice.Slice[string]{"banana"} // Verify that the obtained keys match the expected keys. @@ -762,7 +874,7 @@ func TestLength(t *testing.T) { // Get the length of the hashtable. length := ht.Length() - // Expected length: 3 + // Expected length: 3. expectedLength := 3 // Verify that the obtained length matches the expected length. @@ -817,9 +929,9 @@ func TestMapBreak(t *testing.T) { // 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, false // Break the iteration when key is "banana". } - return value * 2, true // Continue iterating for other keys and double the values + return value * 2, true // Continue iterating for other keys and double the values. }) // Check if values are not modified as expected. @@ -831,3 +943,224 @@ func TestMapBreak(t *testing.T) { } } } + +// TestNot tests Hashtable.Not. +func TestNot(t *testing.T) { + // Test case 1: Check if a key is not present in an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + result := ht.Not("apple") // Check if "apple" is not in the hashtable. + expected := true // "apple" is not present in the empty hashtable. + + if result != expected { + t.Errorf("Expected result to be %v for key 'apple', but got %v", expected, result) + } + + // Test case 2: Check if a key is not present in a non-empty hashtable. + ht.Add("orange", 5) + ht.Add("banana", 10) + result = ht.Not("banana") // Check if "banana" is not in the hashtable. + expected = false // "banana" is present in the hashtable. + + if result != expected { + t.Errorf("Expected result to be %v for key 'banana', but got %v", expected, result) + } + + // Test case 3: Check if a key is not present after removing it from the hashtable. + ht.Delete("banana") // Delete "banana" from the hashtable. + result = ht.Not("banana") + expected = true // "banana" is not present after removal. + + if result != expected { + t.Errorf("Expected result to be %v for key 'banana' after removal, but got %v", expected, result) + } +} + +// TestPop tests Hashtable.Pop. +func TestPop(t *testing.T) { + // Test case 1: Pop from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + removedValue, ok := ht.Pop("apple") + if ok || removedValue != 0 { + t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) + } + + // Test case 2: Pop from a non-empty hashtable where the key is present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + removedValue, ok = ht.Pop("apple") + if !ok || removedValue != 5 { + t.Errorf("Expected (5, true), but got (%d, %v)", removedValue, ok) + } + // Verify that the key is removed. + _, ok = ht.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + + // Test case 3: Pop from a non-empty hashtable where the key is not present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + removedValue, ok = ht.Pop("banana") + if ok || removedValue != 0 { + t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) + } +} + +func TestPopMany(t *testing.T) { + // Test case 1: PopMany from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + removedValues := ht.PopMany("apple", "orange") + expectedValues := &slice.Slice[int]{} + if !reflect.DeepEqual(removedValues, expectedValues) { + t.Errorf("Expected %v, but got %v", expectedValues, removedValues) + } + + // Test case 2: PopMany from a non-empty hashtable where some keys are present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("banana", 3) + ht.Add("cherry", 8) + removedValues = ht.PopMany("apple", "orange", "cherry", "grape") + expectedValues = &slice.Slice[int]{5, 8} + if !reflect.DeepEqual(removedValues, expectedValues) { + t.Errorf("Expected %v, but got %v", expectedValues, removedValues) + } + // Verify that the keys are removed. + _, ok := ht.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + _, ok = ht.Get("orange") + if ok { + t.Errorf("Expected key 'orange' to be removed, but it was found") + } + _, ok = ht.Get("cherry") + if ok { + t.Errorf("Expected key 'cherry' to be removed, but it was found") + } + _, ok = ht.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be removed, but it was found") + } +} + +// TestUpdate tests Hashtable.Update. +func TestUpdate(t *testing.T) { + // Test case 1: Update with an empty hashtable and a function that never modifies any pairs. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + updateFunc := func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + ht.Update(updateFunc) + expected := &hashtable.Hashtable[string, int]{} // Expected empty hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } + + // Test case 2: Update with a non-empty hashtable and a function that never modifies any pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + updateFunc = func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + ht.Update(updateFunc) + expected = &hashtable.Hashtable[string, int]{"apple": 5, "orange": 10} // Expected same hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } + + // Test case 3: Update with a non-empty hashtable and a function that modifies certain pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + updateFunc = func(key string, value int) (int, bool) { + if key == "apple" { + return value * 2, true // Modify the value for the "apple" key + } + return value, false // Leave other values unchanged + } + ht.Update(updateFunc) + expected = &hashtable.Hashtable[string, int]{"apple": 10, "orange": 10} // Expected modified hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } +} + +// TestValues tests Hashtable.Values. +func TestValues(t *testing.T) { + // Test case 1: Values of an empty hashtable should be an empty slice. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + values := ht.Values() + expected := &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 2: Values of a non-empty hashtable. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + values = ht.Values() + sort.Ints(*values) + expected = &slice.Slice[int]{5, 10} // Expected slice containing [5, 10]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 3: Values of a hashtable with multiple entries. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + ht.Add("banana", 15) + values = ht.Values() + + sort.Ints(*values) + expected = &slice.Slice[int]{5, 10, 15} // Expected slice containing [5, 10, 15]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } +} + +// TestValuesFunc tests Hashtable.ValuesFunc. +func TestValuesFunc(t *testing.T) { + // Test case 1: ValuesFunc with an empty hashtable and a condition that never satisfies. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + filterFunc := func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values := ht.ValuesFunc(filterFunc) + expected := &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 2: ValuesFunc with a non-empty hashtable and a condition that never satisfies. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 2) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = ht.ValuesFunc(filterFunc) + expected = &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 3: ValuesFunc with a non-empty hashtable and a condition that satisfies for some values. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + ht.Add("banana", 15) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = ht.ValuesFunc(filterFunc) + sort.Ints(*values) + expected = &slice.Slice[int]{10, 15} // Expected slice containing [10, 15]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } +} From ee0b26cf3d27b03526868d057990d31176b874e8 Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 25 Oct 2023 14:22:11 +1100 Subject: [PATCH 5/6] Dev --- hashtable.go | 110 +++++++++++++++++++++++---------------- hashtable_test.go | 129 ++++++++++++---------------------------------- 2 files changed, 98 insertions(+), 141 deletions(-) diff --git a/hashtable.go b/hashtable.go index 2f15914..f5e5fec 100644 --- a/hashtable.go +++ b/hashtable.go @@ -23,27 +23,6 @@ func (hashtable *Hashtable[K, V]) Add(key K, value V) *Hashtable[K, V] { return hashtable } -// AddFunc inserts key-value pairs into the hashtable based on the values provided in the input slice of maps. -// It applies the provided function to each key-value pair and adds them to the hashtable if the function returns true. -// If the key already exists in the hashtable, the corresponding value is updated with the new value from the input. -// -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.AddFunc([]map[string]int{{"apple": 1, "orange": 2}}, func(key string, value int) bool { -// // Only add key-value pairs where the value is greater than 1. -// return value > 1 -// }) -// fmt.Println(newHashtable) // &map[orange:10] -func (hashtable *Hashtable[K, V]) AddFunc(values []map[K]V, fn func(key K, value V) bool) *Hashtable[K, V] { - for _, item := range values { - for key, value := range item { - if fn(key, value) { - hashtable.Add(key, value) - } - } - } - return hashtable -} - // AddLength inserts a new key-value pair into the hashtable or updates the existing value associated with the provided key. // If the key already exists, the corresponding value is updated. If the key is new, a new key-value pair is added to the hashtable. // It then returns the current length of the hashtable after the addition or update operation. @@ -147,27 +126,6 @@ func (hashtable *Hashtable[K, V]) Delete(key K) *Hashtable[K, V] { return hashtable } -// DeleteFunc removes key-value pairs from the hashtable based on the provided function. The function is applied to each key-value pair, -// and if it returns true, the corresponding key-value pair is deleted from the hashtable. -// -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // Delete key-value pairs where the value is less than 4. -// newHashtable.DeleteFunc(func(key string, value int) bool { -// return value < 4 -// }) -// fmt.Println(newHashtable) // &map[apple:5] -func (hashtable *Hashtable[K, V]) DeleteFunc(fn func(key K, value V) bool) *Hashtable[K, V] { - for key, value := range *hashtable { - if fn(key, value) { - hashtable.Delete(key) - } - } - return hashtable -} - // DeleteLength removes a key-value pair from the hashtable based on the provided key. If the key exists in the hashtable, // it is deleted, and the current length of the hashtable after the deletion is returned. If the key is not found, // the hashtable remains unchanged, and the current length is returned. @@ -201,6 +159,27 @@ func (hashtable *Hashtable[K, V]) DeleteMany(keys ...K) *Hashtable[K, V] { return hashtable } +// DeleteFunc removes key-value pairs from the hashtable based on the provided function. The function is applied to each key-value pair, +// and if it returns true, the corresponding key-value pair is deleted from the hashtable. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// +// // Delete key-value pairs where the value is less than 4. +// newHashtable.DeleteFunc(func(key string, value int) bool { +// return value < 4 +// }) +// fmt.Println(newHashtable) // &map[apple:5] +func (hashtable *Hashtable[K, V]) DeleteManyFunc(fn func(key K, value V) bool) *Hashtable[K, V] { + for key, value := range *hashtable { + if fn(key, value) { + hashtable.Delete(key) + } + } + return hashtable +} + // DeleteManyOK removes multiple key-value pairs from the hashtable based on the provided keys. If a key exists in the hashtable, // it is deleted, and true is appended to the result slice to indicate a successful deletion. If the key is not found, false is appended. // @@ -490,6 +469,19 @@ func (hashtable *Hashtable[K, V]) HasMany(keys ...K) *slice.Slice[bool] { return &values } +// IsEmpty checks if the hashtable is empty, i.e., it contains no key-value pairs. +// It returns true if the hashtable is empty and false otherwise. +// +// ht := make(Hashtable[string, int]) +// empty := ht.IsEmpty() // Returns true since the hashtable is empty +func (hashtable *Hashtable[K, V]) IsEmpty() bool { + return hashtable.Length() == 0 +} + +func (hashtable *Hashtable[K, V]) IsPopulated() bool { + return !hashtable.IsEmpty() +} + // Keys returns a slice containing all the keys present in the hashtable. // // newHashtable := make(hashtable.Hashtable[string, int]) @@ -597,6 +589,23 @@ func (hashtable *Hashtable[K, V]) Not(key K) bool { return !hashtable.Has(key) } +// NotMany checks if multiple keys are not present in the hashtable. +// It takes a variadic number of keys as input and returns a slice of booleans indicating whether each key is not found in the hashtable. +// For each key, if it is not present in the hashtable, the corresponding boolean in the returned slice is true. Otherwise, it is false. +// +// ht := make(Hashtable[string, int]) +// results := ht.NotMany("apple", "orange", "banana") +// // Returns a slice containing [true, true, false] indicating "apple" and "orange" are not in the hashtable, but "banana" is present +func (hashtable *Hashtable[K, V]) NotMany(keys ...K) *slice.Slice[bool] { + values := make(slice.Slice[bool], len(keys)) + for i, key := range keys { + if hashtable.Not(key) { + values.Replace(i, true) + } + } + return &values +} + // Pop removes a key-value pair from the hashtable based on the provided key. // It returns the removed value and a boolean indicating whether the key was found and removed successfully. // If the key is present in the hashtable, the corresponding value is returned, and the key-value pair is deleted. @@ -634,7 +643,18 @@ func (hashtable *Hashtable[K, V]) PopMany(keys ...K) *slice.Slice[V] { return &values } -// Update iterates over the key-value pairs in the hashtable and applies the provided function to each pair. +func (hashtable *Hashtable[K, V]) PopManyFunc(fn func(key K, value V) bool) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + hashtable.Each(func(key K, value V) { + if fn(key, value) { + value, _ := hashtable.Pop(key) + values.Append(value) + } + }) + return &values +} + +// ReplaceMany 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 the update should be performed. // If the function returns true, the key-value pair is updated in the same hashtable with the modified value. // If the function returns false, the key-value pair is not modified. @@ -642,14 +662,14 @@ func (hashtable *Hashtable[K, V]) PopMany(keys ...K) *slice.Slice[V] { // newHashtable := make(hashtable.Hashtable[string, int]) // newHashtable.Add("apple", 5) // newHashtable.Add("banana", 3) -// newHashtable.Update(func(key string, value int) (int, bool) { +// newHashtable.Replace(func(key string, value int) (int, bool) { // if key == "banana" { // return value * 2, true // Modify the value for the "banana" key // } // return value, false // Leave other values unchanged // }) // // newHashtable: {"apple": 5, "banana": 6} -func (hashtable *Hashtable[K, V]) Update(fn func(key K, value V) (V, bool)) *Hashtable[K, V] { +func (hashtable *Hashtable[K, V]) ReplaceMany(fn func(key K, value V) (V, bool)) *Hashtable[K, V] { for key, value := range *hashtable { if updatedValue, ok := fn(key, value); ok { hashtable.Add(key, updatedValue) diff --git a/hashtable_test.go b/hashtable_test.go index c829169..a14bd73 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -34,7 +34,7 @@ func TestAdd(t *testing.T) { t.Errorf("Expected key 'banana' to have value 3, but got %v", value) } - // Test case 2: Update existing key-value pair. + // Test case 2: Replace existing key-value pair. ht.Add("banana", 10) // Updates the value for the key "banana" to 10. value, ok = ht.Get("banana") if !ok || value != 10 { @@ -55,39 +55,6 @@ func TestAdd(t *testing.T) { } } -// TestAddFunc tests Hashtable.AddFunc. -func TestAddFunc(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - - // Test case 1: Add key-value pairs where the value is greater than 1. - ht.AddFunc([]map[string]int{{"apple": 1, "orange": 2, "banana": 3}}, func(key string, value int) bool { - return value > 1 - }) - - // Check if expected key-value pairs are added. - expected := map[string]int{"orange": 2, "banana": 3} - for key, expectedValue := range expected { - value, exists := ht.Get(key) - if !exists || value != expectedValue { - t.Fatalf("Expected key '%s' with value '%d', but got value '%d'", key, expectedValue, value) - } - } - - // Test case 2: Add all key-value pairs without any validation. - ht.AddFunc([]map[string]int{{"kiwi": 0, "pear": 4}}, func(key string, value int) bool { - return true - }) - - // Check if all key-value pairs are added. - allValues := map[string]int{"orange": 2, "banana": 3, "kiwi": 0, "pear": 4} - for key, expectedValue := range allValues { - value, exists := ht.Get(key) - if !exists || value != expectedValue { - t.Fatalf("Expected key '%s' with value '%d', but got value '%d'", key, expectedValue, value) - } - } -} - // TestAddLength tests Hashtable.AddLength. func TestAddLength(t *testing.T) { // Test case 1: Add key-value pairs to an empty hashtable. @@ -97,7 +64,7 @@ func TestAddLength(t *testing.T) { t.Errorf("Expected length of hashtable after adding 'apple' with value 5 to be 1, but got %v", length) } - // Test case 2: Update an existing key-value pair. + // Test case 2: Replace an existing key-value pair. length = ht.AddLength("apple", 10) if length != 1 { t.Errorf("Expected length of hashtable after updating 'apple' with value 10 to be 1, but got %v", length) @@ -141,7 +108,7 @@ func TestAddMany(t *testing.T) { } } - // Test case 2: Update existing key-value pairs and add new ones. + // Test case 2: Replace existing key-value pairs and add new ones. ht.AddMany(map[string]int{"orange": 10, "grape": 3}, map[string]int{"apple": 5, "banana": 8}) // Verify the updated and added key-value pairs. @@ -327,36 +294,6 @@ func TestDelete(t *testing.T) { } } -// TestDeleteFunc tests Hashtable.DeleteFunc. -func TestDeleteFunc(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["orange"] = 7 - ht["kiwi"] = 6 - - // Delete key-value pairs where the value is 7. - ht.DeleteFunc(func(key string, value int) bool { - return value == 6 - }) - - // Check if the key-value pair with key "kiwi" is removed. - _, exists := ht["kiwi"] - if exists { - t.Error("Expected key 'kiwi' to be removed, but it still exists.") - } - - // Check if other key-value pairs are still present. - _, exists = ht["apple"] - if !exists { - t.Error("Expected key 'apple' to be present, but it is not.") - } - - _, exists = ht["orange"] - if !exists { - t.Error("Expected key 'orange' to be present, but it is not.") - } -} - // TestDeleteLength tests Hashtable.DeleteLength. func TestDeleteLength(t *testing.T) { // Create a new hashtable. @@ -393,30 +330,30 @@ func TestDeleteLength(t *testing.T) { // TestDeleteMany tests Hashtable.DeleteMany. func TestDeleteMany(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["orange"] = 7 - ht["kiwi"] = 6 - - // Delete key-value pairs with keys "apple" and "kiwi". - ht.DeleteMany("apple", "kiwi") + // Test case 1: Delete keys from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.DeleteMany("apple", "banana") // Attempt to delete keys "apple" and "banana". - // Check if the key-value pair with key "apple" is removed. - _, exists := ht["apple"] - if exists { - t.Error("Expected key 'apple' to be removed, but it still exists.") + // The hashtable should remain empty. + if !ht.IsEmpty() { + t.Errorf("Expected hashtable to be empty, but got non-empty hashtable: %v", ht) } - // Check if the key-value pair with key "kiwi" is removed. - _, exists = ht["kiwi"] - if exists { - t.Error("Expected key 'kiwi' to be removed, but it still exists.") - } + // Test case 2: Delete keys from a non-empty hashtable. + ht = &hashtable.Hashtable[string, int]{} // Create a new hashtable. + ht.Add("apple", 5) + ht.Add("banana", 10) + ht.Add("orange", 3) - // Check if other key-value pairs are still present. - _, exists = ht["orange"] - if !exists { - t.Error("Expected key 'orange' to be present, but it is not.") + // Delete keys "apple" and "banana". + ht.DeleteMany("apple", "banana") + + // Verify that "apple" and "banana" are deleted, and "orange" remains in the hashtable. + expected := &slice.Slice[string]{"orange"} + result := ht.Keys() + + if !result.Equal(expected) { + t.Errorf("Expected keys %v after deleting 'apple' and 'banana', but got keys %v", expected, result) } } @@ -1045,43 +982,43 @@ func TestPopMany(t *testing.T) { } } -// TestUpdate tests Hashtable.Update. +// TestReplaceMany tests Hashtable.ReplaceMany. func TestUpdate(t *testing.T) { - // Test case 1: Update with an empty hashtable and a function that never modifies any pairs. + // Test case 1: Replace with an empty hashtable and a function that never modifies any pairs. ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. - updateFunc := func(key string, value int) (int, bool) { + replaceFunc := func(key string, value int) (int, bool) { return value, false // Never modify any values } - ht.Update(updateFunc) + ht.ReplaceMany(replaceFunc) expected := &hashtable.Hashtable[string, int]{} // Expected empty hashtable. if !reflect.DeepEqual(ht, expected) { t.Errorf("Expected %v, but got %v", expected, ht) } - // Test case 2: Update with a non-empty hashtable and a function that never modifies any pairs. + // Test case 2: Replace with a non-empty hashtable and a function that never modifies any pairs. ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. ht.Add("apple", 5) ht.Add("orange", 10) - updateFunc = func(key string, value int) (int, bool) { + replaceFunc = func(key string, value int) (int, bool) { return value, false // Never modify any values } - ht.Update(updateFunc) + ht.ReplaceMany(replaceFunc) expected = &hashtable.Hashtable[string, int]{"apple": 5, "orange": 10} // Expected same hashtable. if !reflect.DeepEqual(ht, expected) { t.Errorf("Expected %v, but got %v", expected, ht) } - // Test case 3: Update with a non-empty hashtable and a function that modifies certain pairs. + // Test case 3: Replace with a non-empty hashtable and a function that modifies certain pairs. ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. ht.Add("apple", 5) ht.Add("orange", 10) - updateFunc = func(key string, value int) (int, bool) { + replaceFunc = func(key string, value int) (int, bool) { if key == "apple" { return value * 2, true // Modify the value for the "apple" key } return value, false // Leave other values unchanged } - ht.Update(updateFunc) + ht.ReplaceMany(replaceFunc) expected = &hashtable.Hashtable[string, int]{"apple": 10, "orange": 10} // Expected modified hashtable. if !reflect.DeepEqual(ht, expected) { t.Errorf("Expected %v, but got %v", expected, ht) From a68e543ab02d18763a2913013e5274db781d81ca Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Wed, 25 Oct 2023 14:24:02 +1100 Subject: [PATCH 6/6] Dev --- hashtable.go | 219 ++++++++++++++++++++------------- hashtable_test.go | 299 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 381 insertions(+), 137 deletions(-) diff --git a/hashtable.go b/hashtable.go index 1700382..f5e5fec 100644 --- a/hashtable.go +++ b/hashtable.go @@ -51,31 +51,9 @@ 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). +// AddManyFunc inserts key-value pairs into the hashtable based on a provided condition function. +// It accepts a slice of maps, where each map contains key-value pairs. For each key-value pair, +// the specified function is called. If the function returns true, the pair is added to the hashtable. // // newHashtable := make(hashtable.Hashtable[string, int]) // newHashtable.AddManyFunc([]map[K]V{{"apple": 5, "orange": -3, "banana": 10}}, func(i int, key string, value int) bool { @@ -221,28 +199,8 @@ func (hashtable *Hashtable[K, V]) DeleteManyOK(keys ...K) *slice.Slice[bool] { return &deletetions } -// 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 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: -// -// 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. +// DeleteManyValues removes key-value pairs from the hashtable based on the provided values. If a value exists in the hashtable, +// the corresponding key-value pair is deleted. // // newHashtable := make(hashtable.Hashtable[string, int]) // newHashtable.Add("apple", 5) @@ -262,16 +220,23 @@ func (hashtable *Hashtable[K, V]) DeleteManyValues(values ...V) *Hashtable[K, V] 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. +// DeleteOK removes a key-value pair from the hashtable based on the provided key. If the key exists in the hashtable, +// it is deleted, and true is returned to indicate a successful deletion. If the key is not found, the hashtable remains unchanged, +// and false is returned to indicate that the deletion failed. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) // -// 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 +// // Attempt to delete the key-value pair with the key "apple". +// success := newHashtable.DeleteOK("apple") +// // After deletion, the key "apple" is not found in the hashtable. +// // Deletion success: true +// +// // Attempt to delete a non-existing key. +// success = newHashtable.DeleteOK("orange") +// // The key "orange" does not exist in the hashtable. +// // Deletion failed: false func (hashtable *Hashtable[K, V]) DeleteOK(key K) bool { return !hashtable.Delete(key).Has(key) } @@ -568,45 +533,41 @@ func (hashtable *Hashtable[K, V]) Length() int { return len(*hashtable) } -// 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. +// Map applies the provided function to each key-value pair in the hashtable and returns a new hashtable containing the mapped key-value pairs. +// The original hashtable remains unchanged. // // newHashtable := make(hashtable.Hashtable[string, int]) // newHashtable.Add("apple", 5) // newHashtable.Add("banana", 3) // -// ht := make(hashtable.Hashtable[string, int]) -// ht.Add("apple", 5) -// ht.Add("banana", 3) -// 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} +// // Function to double the values of each key-value pair. +// doubleValues := func(key string, value int) int { +// return value * 2 +// } +// +// // Create a new hashtable with doubled values. +// newHashtable := newHashtable.Map(doubleValues) +// // New hashtable: {"apple": 10, "banana": 6} func (hashtable *Hashtable[K, V]) Map(fn func(key K, value V) V) *Hashtable[K, V] { 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. +// MapBreak applies the provided function to each key-value pair in the hashtable. It creates a new hashtable containing the mapped key-value pairs +// until the function returns false for any pair, at which point the mapping breaks early. The original hashtable remains unchanged. // -// Example: +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Add("cherry", 8) // -// 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 +// // Create a new hashtable with doubled values until a value is greater than or equal to 10. +// newHashtable := newHashtable.MapBreak(func(key string, value int) (int, bool) { +// newValue := value * 2 +// return newValue, newValue < 10 // }) -// // newHT: {"apple": 5} +// // New hashtable: {"apple": 10, "banana": 6} 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 { @@ -619,6 +580,104 @@ func (hashtable *Hashtable[K, V]) MapBreak(fn func(key K, value V) (V, bool)) *H return &newHashtable } +// Not checks if the given key is not present in the hashtable. +// It returns true if the key is not found, and false if the key exists in the hashtable. +// +// ht := make(Hashtable[string, int]) +// result := ht.Not("apple") // Returns true if "apple" is not in the hashtable, false otherwise +func (hashtable *Hashtable[K, V]) Not(key K) bool { + return !hashtable.Has(key) +} + +// NotMany checks if multiple keys are not present in the hashtable. +// It takes a variadic number of keys as input and returns a slice of booleans indicating whether each key is not found in the hashtable. +// For each key, if it is not present in the hashtable, the corresponding boolean in the returned slice is true. Otherwise, it is false. +// +// ht := make(Hashtable[string, int]) +// results := ht.NotMany("apple", "orange", "banana") +// // Returns a slice containing [true, true, false] indicating "apple" and "orange" are not in the hashtable, but "banana" is present +func (hashtable *Hashtable[K, V]) NotMany(keys ...K) *slice.Slice[bool] { + values := make(slice.Slice[bool], len(keys)) + for i, key := range keys { + if hashtable.Not(key) { + values.Replace(i, true) + } + } + return &values +} + +// Pop removes a key-value pair from the hashtable based on the provided key. +// It returns the removed value and a boolean indicating whether the key was found and removed successfully. +// If the key is present in the hashtable, the corresponding value is returned, and the key-value pair is deleted. +// If the key is not found, it returns the zero value for the value type and false. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// removedValue, ok := ht.Pop("apple") // Removes the key "apple" and returns its associated value 5, ok is true +// removedValue, ok = ht.Pop("banana") // Key "banana" not found, removedValue is 0 and ok is false +func (hashtable *Hashtable[K, V]) Pop(key K) (V, bool) { + value, ok := hashtable.Get(key) + if ok { + ok = hashtable.DeleteOK(key) + } + return value, ok +} + +// PopMany removes multiple key-value pairs from the hashtable based on the provided keys. +// It takes a variadic number of keys as input and removes the corresponding key-value pairs from the hashtable. +// It returns a slice containing the removed values and does not guarantee any specific order of values in the result. +// If a key is not found in the hashtable, the corresponding value in the result slice will be the zero value for the value type. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// removedValues := ht.PopMany("apple", "orange") // Removes "apple", returns a slice containing [5, 0] +func (hashtable *Hashtable[K, V]) PopMany(keys ...K) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + for _, key := range keys { + value, ok := hashtable.Pop(key) + if ok { + values.Append(value) + } + } + return &values +} + +func (hashtable *Hashtable[K, V]) PopManyFunc(fn func(key K, value V) bool) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + hashtable.Each(func(key K, value V) { + if fn(key, value) { + value, _ := hashtable.Pop(key) + values.Append(value) + } + }) + return &values +} + +// ReplaceMany 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 the update should be performed. +// If the function returns true, the key-value pair is updated in the same hashtable with the modified value. +// If the function returns false, the key-value pair is not modified. +// +// newHashtable := make(hashtable.Hashtable[string, int]) +// newHashtable.Add("apple", 5) +// newHashtable.Add("banana", 3) +// newHashtable.Replace(func(key string, value int) (int, bool) { +// if key == "banana" { +// return value * 2, true // Modify the value for the "banana" key +// } +// return value, false // Leave other values unchanged +// }) +// // newHashtable: {"apple": 5, "banana": 6} +func (hashtable *Hashtable[K, V]) ReplaceMany(fn func(key K, value V) (V, bool)) *Hashtable[K, V] { + for key, value := range *hashtable { + if updatedValue, ok := fn(key, value); ok { + hashtable.Add(key, updatedValue) + } + } + return hashtable +} + // Values returns a slice containing all the values present in the hashtable. // It iterates over the hashtable and collects all the values in the order of insertion. // diff --git a/hashtable_test.go b/hashtable_test.go index 98abfa7..a14bd73 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -227,42 +227,6 @@ func TestAddManyOK(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.Fatalf("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.Fatalf("Expected result: %v, but got: %v", expectedResults[i], result) - } - } -} - // TestAddOK tests Hashtable.AddOK. func TestAddOK(t *testing.T) { ht := make(hashtable.Hashtable[string, int]) @@ -393,24 +357,6 @@ func TestDeleteMany(t *testing.T) { } } -// TestDeleteManyValues tests Hashtable.DeleteManyValues. -func TestDeleteManyValues(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - ht.Add("apple", 5) - ht.Add("banana", 3) - ht.Add("cherry", 8) - - // Delete key-value pairs where the value is 3 or 8. - ht.DeleteManyValues(3, 8) - - // Verify that the hashtable only contains the expected key-value pair. - expected := hashtable.Hashtable[string, int]{"apple": 5} - if !reflect.DeepEqual(ht, expected) { - t.Fatalf("Expected hashtable: %v, but got: %v", expected, ht) - } -} - // TestDeleteManyOK tests Hashtable.DeleteManyOK. func TestDeleteManyOK(t *testing.T) { // Create a new hashtable. @@ -426,7 +372,7 @@ func TestDeleteManyOK(t *testing.T) { // 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) + 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 { @@ -436,6 +382,24 @@ func TestDeleteManyOK(t *testing.T) { } } +// TestDeleteManyValues tests Hashtable.DeleteManyValues. +func TestDeleteManyValues(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + ht.Add("apple", 5) + ht.Add("banana", 3) + ht.Add("cherry", 8) + + // Delete key-value pairs where the value is 3 or 8. + ht.DeleteManyValues(3, 8) + + // Verify that the hashtable only contains the expected key-value pair. + expected := hashtable.Hashtable[string, int]{"apple": 5} + if !reflect.DeepEqual(ht, expected) { + t.Fatalf("Expected hashtable: %v, but got: %v", expected, ht) + } +} + // TestDeleteOK tests Hashtable.DeleteOK. func TestDeleteOK(t *testing.T) { // Create a new hashtable. @@ -902,9 +866,9 @@ func TestMapBreak(t *testing.T) { // 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, false // Break the iteration when key is "banana". } - return value * 2, true // Continue iterating for other keys and double the values + return value * 2, true // Continue iterating for other keys and double the values. }) // Check if values are not modified as expected. @@ -916,3 +880,224 @@ func TestMapBreak(t *testing.T) { } } } + +// TestNot tests Hashtable.Not. +func TestNot(t *testing.T) { + // Test case 1: Check if a key is not present in an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + result := ht.Not("apple") // Check if "apple" is not in the hashtable. + expected := true // "apple" is not present in the empty hashtable. + + if result != expected { + t.Errorf("Expected result to be %v for key 'apple', but got %v", expected, result) + } + + // Test case 2: Check if a key is not present in a non-empty hashtable. + ht.Add("orange", 5) + ht.Add("banana", 10) + result = ht.Not("banana") // Check if "banana" is not in the hashtable. + expected = false // "banana" is present in the hashtable. + + if result != expected { + t.Errorf("Expected result to be %v for key 'banana', but got %v", expected, result) + } + + // Test case 3: Check if a key is not present after removing it from the hashtable. + ht.Delete("banana") // Delete "banana" from the hashtable. + result = ht.Not("banana") + expected = true // "banana" is not present after removal. + + if result != expected { + t.Errorf("Expected result to be %v for key 'banana' after removal, but got %v", expected, result) + } +} + +// TestPop tests Hashtable.Pop. +func TestPop(t *testing.T) { + // Test case 1: Pop from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + removedValue, ok := ht.Pop("apple") + if ok || removedValue != 0 { + t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) + } + + // Test case 2: Pop from a non-empty hashtable where the key is present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + removedValue, ok = ht.Pop("apple") + if !ok || removedValue != 5 { + t.Errorf("Expected (5, true), but got (%d, %v)", removedValue, ok) + } + // Verify that the key is removed. + _, ok = ht.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + + // Test case 3: Pop from a non-empty hashtable where the key is not present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + removedValue, ok = ht.Pop("banana") + if ok || removedValue != 0 { + t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) + } +} + +func TestPopMany(t *testing.T) { + // Test case 1: PopMany from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + removedValues := ht.PopMany("apple", "orange") + expectedValues := &slice.Slice[int]{} + if !reflect.DeepEqual(removedValues, expectedValues) { + t.Errorf("Expected %v, but got %v", expectedValues, removedValues) + } + + // Test case 2: PopMany from a non-empty hashtable where some keys are present. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("banana", 3) + ht.Add("cherry", 8) + removedValues = ht.PopMany("apple", "orange", "cherry", "grape") + expectedValues = &slice.Slice[int]{5, 8} + if !reflect.DeepEqual(removedValues, expectedValues) { + t.Errorf("Expected %v, but got %v", expectedValues, removedValues) + } + // Verify that the keys are removed. + _, ok := ht.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + _, ok = ht.Get("orange") + if ok { + t.Errorf("Expected key 'orange' to be removed, but it was found") + } + _, ok = ht.Get("cherry") + if ok { + t.Errorf("Expected key 'cherry' to be removed, but it was found") + } + _, ok = ht.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be removed, but it was found") + } +} + +// TestReplaceMany tests Hashtable.ReplaceMany. +func TestUpdate(t *testing.T) { + // Test case 1: Replace with an empty hashtable and a function that never modifies any pairs. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + replaceFunc := func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + ht.ReplaceMany(replaceFunc) + expected := &hashtable.Hashtable[string, int]{} // Expected empty hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } + + // Test case 2: Replace with a non-empty hashtable and a function that never modifies any pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + replaceFunc = func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + ht.ReplaceMany(replaceFunc) + expected = &hashtable.Hashtable[string, int]{"apple": 5, "orange": 10} // Expected same hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } + + // Test case 3: Replace with a non-empty hashtable and a function that modifies certain pairs. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + replaceFunc = func(key string, value int) (int, bool) { + if key == "apple" { + return value * 2, true // Modify the value for the "apple" key + } + return value, false // Leave other values unchanged + } + ht.ReplaceMany(replaceFunc) + expected = &hashtable.Hashtable[string, int]{"apple": 10, "orange": 10} // Expected modified hashtable. + if !reflect.DeepEqual(ht, expected) { + t.Errorf("Expected %v, but got %v", expected, ht) + } +} + +// TestValues tests Hashtable.Values. +func TestValues(t *testing.T) { + // Test case 1: Values of an empty hashtable should be an empty slice. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + values := ht.Values() + expected := &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 2: Values of a non-empty hashtable. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + values = ht.Values() + sort.Ints(*values) + expected = &slice.Slice[int]{5, 10} // Expected slice containing [5, 10]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 3: Values of a hashtable with multiple entries. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + ht.Add("banana", 15) + values = ht.Values() + + sort.Ints(*values) + expected = &slice.Slice[int]{5, 10, 15} // Expected slice containing [5, 10, 15]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } +} + +// TestValuesFunc tests Hashtable.ValuesFunc. +func TestValuesFunc(t *testing.T) { + // Test case 1: ValuesFunc with an empty hashtable and a condition that never satisfies. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + filterFunc := func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values := ht.ValuesFunc(filterFunc) + expected := &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 2: ValuesFunc with a non-empty hashtable and a condition that never satisfies. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 2) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = ht.ValuesFunc(filterFunc) + expected = &slice.Slice[int]{} // Expected empty slice. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } + + // Test case 3: ValuesFunc with a non-empty hashtable and a condition that satisfies for some values. + ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + ht.Add("apple", 5) + ht.Add("orange", 10) + ht.Add("banana", 15) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = ht.ValuesFunc(filterFunc) + sort.Ints(*values) + expected = &slice.Slice[int]{10, 15} // Expected slice containing [10, 15]. + if !reflect.DeepEqual(values, expected) { + t.Errorf("Expected %v, but got %v", expected, values) + } +}