diff --git a/hashtable.go b/hashtable.go index 35608df..5254665 100644 --- a/hashtable.go +++ b/hashtable.go @@ -397,3 +397,86 @@ func (hashtable *Hashtable[K, V]) HasMany(keys ...K) *slice.Slice[bool] { } return &values } + +// 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) +// +// // Get all keys from the hashtable. +// keys := ht.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) { + keys.Append(key) + }) + 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. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// ht.Add("cherry", 8) +// +// // Get keys from the hashtable where the key length is greater than 5. +// keys := ht.KeysFunc(func(key string) bool { +// return len(key) > 5 +// }) +// // Result: {"banana"} +func (hashtable *Hashtable[K, V]) KeysFunc(fn func(key K) bool) *slice.Slice[K] { + keys := make(slice.Slice[K], 0) + hashtable.EachKey(func(key K) { + if fn(key) { + keys.Append(key) + } + }) + return &keys +} + +// Length returns the number of key-value pairs in the hashtable. +// +// Example: +// +// ht := make(hashtable.Hashtable[string, int]) +// ht.Add("apple", 5) +// ht.Add("banana", 3) +// ht.Add("cherry", 8) +// +// length := ht.Length() // Result: 3 +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. +// +// 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} +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 +} diff --git a/hashtable_test.go b/hashtable_test.go index 78e6bfc..a7b035a 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -3,6 +3,7 @@ package hashtable_test import ( "reflect" "sort" + "strings" "testing" "github.com/lindsaygelle/hashtable" @@ -424,11 +425,12 @@ func TestEachValueBreak(t *testing.T) { ht.Add("banana", 3) ht.Add("cherry", 8) - // Sort the keys for consistent iteration order. keys := make([]string, 0, len(ht)) for key := range ht { keys = append(keys, key) } + + // Sort the keys for consistent iteration order. sort.Strings(keys) // Define a function to process each value. It returns false to break the iteration if the value is 3. @@ -529,9 +531,9 @@ func TestHas(t *testing.T) { func TestHasMany(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) + ht["apple"] = 5 + ht["banana"] = 3 + ht["cherry"] = 8 // Keys to check existence. keysToCheck := []string{"apple", "orange", "banana"} @@ -547,3 +549,71 @@ func TestHasMany(t *testing.T) { t.Fatalf("Expected results: %v, but got: %v", expectedResults, results) } } + +// TestKeys tests Hashtable.Keys. +func TestKeys(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + ht["apple"] = 5 + ht["banana"] = 3 + ht["cherry"] = 8 + + // Get all keys from the hashtable. + keys := ht.Keys() + + // Sort the keys for consistent iteration order. + sort.Strings(*keys) + + // The expected keys slice: {"apple", "banana", "cherry"} + expectedKeys := &slice.Slice[string]{"apple", "banana", "cherry"} + + // Sort the keys for consistent iteration order. + sort.Strings(*expectedKeys) + + // Verify that the obtained keys match the expected keys. + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("Expected keys: %v, but got: %v", expectedKeys, keys) + } +} + +// TestKeysFunc tests Hashtable.KeysFunc. +func TestKeysFunc(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + ht["apple"] = 5 + ht["banana"] = 3 + ht["cherry"] = 8 + + // Get keys from the hashtable where the key length is greater than 5. + keys := ht.KeysFunc(func(key string) bool { + return strings.HasPrefix(key, "b") + }) + + // The expected keys slice: {"banana"} + expectedKeys := &slice.Slice[string]{"banana"} + + // Verify that the obtained keys match the expected keys. + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("Expected keys: %v, but got: %v", expectedKeys, keys) + } +} + +// TestLength tests Hashtable.Length. +func TestLength(t *testing.T) { + // Create a new hashtable. + ht := make(hashtable.Hashtable[string, int]) + ht["apple"] = 5 + ht["banana"] = 3 + ht["cherry"] = 8 + + // Get the length of the hashtable. + length := ht.Length() + + // Expected length: 3 + expectedLength := 3 + + // Verify that the obtained length matches the expected length. + if length != expectedLength { + t.Errorf("Expected length: %d, but got: %d", expectedLength, length) + } +}