From 38be94ae3b2b5ca9d55b8ea58c664490dad75b02 Mon Sep 17 00:00:00 2001 From: lindsaygelle Date: Thu, 26 Oct 2023 14:54:27 +1100 Subject: [PATCH] Dev --- hashtable.go | 59 ++++++++++++++++++++--- hashtable_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 166 insertions(+), 9 deletions(-) diff --git a/hashtable.go b/hashtable.go index d1237ab..ee5ab9a 100644 --- a/hashtable.go +++ b/hashtable.go @@ -396,6 +396,23 @@ func (hashtable *Hashtable[K, V]) EachValueBreak(fn func(value V) bool) *Hashtab }) } +// EmptyInto transfers all key-value pairs from the current hashtable into another hashtable, emptying the current hashtable. +// It takes another hashtable as input and adds all key-value pairs from the current hashtable to the other hashtable. +// +// // Create a new Hashtable instance. +// ht1 := make(Hashtable[string, int]) +// ht1.Add("apple", 5) +// // Create a new Hashtable instance. +// ht2 := make(Hashtable[string, int]) +// +// ht1.EmptyInto(ht2) // Transfers "apple": 5 from ht1 to ht2, leaving ht1 empty +func (hashtable *Hashtable[K, V]) EmptyInto(otherHashtable *Hashtable[K, V]) *Hashtable[K, V] { + hashtable.Each(func(key K, value V) { + otherHashtable.Add(key, hashtable.Pop(key)) + }) + return hashtable +} + // Equal checks if the current hashtable is equal to another hashtable by comparing the key-value pairs directly using reflect.DeepEqual. // It takes another hashtable as input and returns true if the two hashtables are equal, false otherwise. // @@ -822,16 +839,28 @@ func (hashtable *Hashtable[K, V]) NotMany(keys ...K) *slice.Slice[bool] { return &values } -// Pop removes a key-value pair from the hashtable based on the provided key. +// Pop removes a key-value pair from the hashtable based on the provided key and returns the removed value. +// If the key is found in the hashtable, the corresponding value is returned. If the key is not present, +// the zero value for the value type is returned. +// +// ht := make(Hashtable[string, int]) +// ht.Add("apple", 5) +// removedValue := ht.Pop("apple") // Removes the key "apple" and returns its associated value 5, or 0 if "apple" is not found +func (hashtable *Hashtable[K, V]) Pop(key K) V { + value, _ := hashtable.PopOK(key) + return value +} + +// PopOK 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. // // newHashtable := make(Hashtable[string, int]) // newHashtable.Add("apple", 5) -// removedValue, ok := newHashtable.Pop("apple") // Removes the key "apple" and returns its associated value 5, ok is true -// removedValue, ok = newHashtable.Pop("banana") // Key "banana" not found, removedValue is 0 and ok is false -func (hashtable *Hashtable[K, V]) Pop(key K) (V, bool) { +// removedValue, ok := newHashtable.PopOK("apple") // Removes the key "apple" and returns its associated value 5, ok is true +// removedValue, ok = newHashtable.PopOK("banana") // Key "banana" not found, removedValue is 0 and ok is false +func (hashtable *Hashtable[K, V]) PopOK(key K) (V, bool) { value, ok := hashtable.Get(key) if ok { ok = hashtable.DeleteOK(key) @@ -851,7 +880,7 @@ func (hashtable *Hashtable[K, V]) Pop(key K) (V, bool) { 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) + value, ok := hashtable.PopOK(key) if ok { values.Append(value) } @@ -874,7 +903,7 @@ func (hashtable *Hashtable[K, V]) PopManyFunc(fn func(key K, value V) bool) *sli values := make(slice.Slice[V], 0) hashtable.Each(func(key K, value V) { if fn(key, value) { - removedValue, _ := hashtable.Pop(key) + removedValue := hashtable.Pop(key) values.Append(removedValue) } }) @@ -906,6 +935,24 @@ func (hashtable *Hashtable[K, V]) ReplaceMany(fn func(key K, value V) (V, bool)) return hashtable } +// TakeFrom transfers all key-value pairs from another hashtable into the current hashtable, emptying the other hashtable. +// It takes another hashtable as input and adds all key-value pairs from the other hashtable to the current hashtable. +// +// // Create a new Hashtable instance. +// ht1 := make(Hashtable[string, int]) +// ht1.Add("apple", 5) +// // Create a new Hashtable instance. +// ht2 := make(Hashtable[string, int]) +// ht2.Add("orange", 10) +// +// ht1.TakeFrom(ht2) // Transfers "orange": 10 from ht2 to ht1, leaving ht2 empty +func (hashtable *Hashtable[K, V]) TakeFrom(otherHashtable *Hashtable[K, V]) *Hashtable[K, V] { + otherHashtable.Each(func(key K, value V) { + hashtable.Add(key, otherHashtable.Pop(key)) + }) + 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 516cad9..60ccf1a 100644 --- a/hashtable_test.go +++ b/hashtable_test.go @@ -655,6 +655,42 @@ func TestEachValueBreak(t *testing.T) { } } +// TestEmptyInto tests Hashtable.EmptyInto. +func TestEmptyInto(t *testing.T) { + // Test case 1: Transfer from an empty hashtable to another empty hashtable. + ht1 := &hashtable.Hashtable[string, int]{} // Create an empty source hashtable. + ht2 := &hashtable.Hashtable[string, int]{} // Create an empty destination hashtable. + ht1.EmptyInto(ht2) // Transfer from ht1 to ht2. + + // Verify that ht1 is empty. + if ht1.Length() != 0 { + t.Errorf("Expected source hashtable to be empty after transfer, but it has %d items", ht1.Length()) + } + + // Verify that ht2 is still empty. + if ht2.Length() != 0 { + t.Errorf("Expected destination hashtable to be empty after transfer, but it has %d items", ht2.Length()) + } + + // Test case 2: Transfer from a non-empty hashtable to an empty hashtable. + ht1 = &hashtable.Hashtable[string, int]{} // Create an empty source hashtable. + ht1.Add("apple", 5) + ht2 = &hashtable.Hashtable[string, int]{} // Create an empty destination hashtable. + ht1.EmptyInto(ht2) // Transfer from ht1 to ht2. + + // Verify that ht1 is empty. + if ht1.Length() != 0 { + t.Errorf("Expected source hashtable to be empty after transfer, but it has %d items", ht1.Length()) + } + + // Verify that ht2 contains the transferred key-value pair. + expectedValue := 5 + transferredValue, ok := ht2.Get("apple") + if !ok || transferredValue != expectedValue { + t.Errorf("Expected destination hashtable to contain 'apple': %d after transfer, but it contains '%d'", expectedValue, transferredValue) + } +} + // TestEqual tests Hashtable.Equal. func TestEqual(t *testing.T) { // Test case 1: Compare equal hashtables. @@ -1253,7 +1289,45 @@ func TestNot(t *testing.T) { 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") + removedValue := ht.Pop("apple") + expectedValue := 0 // No key "apple" in the empty hashtable. + + if removedValue != expectedValue { + t.Errorf("Expected removed value to be %d, but got %d", expectedValue, removedValue) + } + + // 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 = ht.Pop("apple") + expectedValue = 5 // Key "apple" exists with value 5. + + if removedValue != expectedValue { + t.Errorf("Expected removed value to be %d, but got %d", expectedValue, removedValue) + } + // 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 = ht.Pop("banana") + expectedValue = 0 // No key "banana" in the hashtable. + + if removedValue != expectedValue { + t.Errorf("Expected removed value to be %d, but got %d", expectedValue, removedValue) + } +} + +// TestPopOK tests Hashtable.PopOK. +func TestPopOK(t *testing.T) { + // Test case 1: Pop from an empty hashtable. + ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. + removedValue, ok := ht.PopOK("apple") if ok || removedValue != 0 { t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) } @@ -1261,7 +1335,7 @@ func TestPop(t *testing.T) { // 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") + removedValue, ok = ht.PopOK("apple") if !ok || removedValue != 5 { t.Errorf("Expected (5, true), but got (%d, %v)", removedValue, ok) } @@ -1275,7 +1349,7 @@ func TestPop(t *testing.T) { ht = &hashtable.Hashtable[string, int]{} // Create an empty hashtable. ht.Add("apple", 5) ht.Add("orange", 10) - removedValue, ok = ht.Pop("banana") + removedValue, ok = ht.PopOK("banana") if ok || removedValue != 0 { t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) } @@ -1421,6 +1495,42 @@ func TestUpdate(t *testing.T) { } } +// TestTakeFrom tests Hashtable.TakeFrom. +func TestTakeFrom(t *testing.T) { + // Test case 1: Transfer from an empty hashtable to another empty hashtable. + ht1 := &hashtable.Hashtable[string, int]{} // Create an empty destination hashtable. + ht2 := &hashtable.Hashtable[string, int]{} // Create an empty source hashtable. + ht1.TakeFrom(ht2) // Transfer from ht2 to ht1. + + // Verify that ht1 is still empty. + if ht1.Length() != 0 { + t.Errorf("Expected destination hashtable to be empty after transfer, but it has %d items", ht1.Length()) + } + + // Verify that ht2 is still empty. + if ht2.Length() != 0 { + t.Errorf("Expected source hashtable to be empty after transfer, but it has %d items", ht2.Length()) + } + + // Test case 2: Transfer from a non-empty hashtable to an empty hashtable. + ht1 = &hashtable.Hashtable[string, int]{} // Create an empty destination hashtable. + ht2 = &hashtable.Hashtable[string, int]{} // Create a source hashtable. + ht2.Add("orange", 10) + ht1.TakeFrom(ht2) // Transfer from ht2 to ht1. + + // Verify that ht1 contains the transferred key-value pair. + expectedValue := 10 + transferredValue, ok := ht1.Get("orange") + if !ok || transferredValue != expectedValue { + t.Errorf("Expected destination hashtable to contain 'orange': %d after transfer, but it contains '%d'", expectedValue, transferredValue) + } + + // Verify that ht2 is empty after transfer. + if ht2.Length() != 0 { + t.Errorf("Expected source hashtable to be empty after transfer, but it has %d items", ht2.Length()) + } +} + // TestValues tests Hashtable.Values. func TestValues(t *testing.T) { // Test case 1: Values of an empty hashtable should be an empty slice.