diff --git a/queue/ArrayQueue/arrayqueue.go b/queue/ArrayQueue/arrayqueue.go index 4c936b8..81f3895 100644 --- a/queue/ArrayQueue/arrayqueue.go +++ b/queue/ArrayQueue/arrayqueue.go @@ -1,6 +1,8 @@ package arrayqueue -import dsa_error "github.com/hmcalister/Go-DSA/utils/DSA_Error" +import ( + dsa_error "github.com/hmcalister/Go-DSA/utils/DSA_Error" +) // Implement a queue using a array / slice. // @@ -96,3 +98,86 @@ func (queue *ArrayQueue[T]) Remove() (T, error) { queue.queueData = queue.queueData[1:] return item, nil } + +// ---------------------------------------------------------------------------- +// Apply, Map, and Fold methods +// +// Methods to apply a function across ALL items in a queue. + +// Iterate over the queue in the forward direction and apply a function to each item. +// +// It is expected that ForwardApply does *not* update the queue items. +// To modify the queue items, use ForwardMap. +// To accumulate values over the queue, use ForwardFold. +func ForwardApply[T any](queue *ArrayQueue[T], f func(item T)) { + for index := 0; index < len(queue.queueData); index += 1 { + f(queue.queueData[index]) + } +} + +// Iterate over the queue in the forward direction and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ForwardMap can update the node items by returning the update value. +// If you do not need to modify the queue items, use ForwardApply. +// To accumulate values over the queue, use ForwardFold. +func ForwardMap[T any](queue *ArrayQueue[T], f func(item T) T) { + for index := 0; index < len(queue.queueData); index += 1 { + queue.queueData[index] = f(queue.queueData[index]) + } +} + +// Iterate over the queue and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on ArrayQueue to allow for generic accumulators. +func ForwardFold[T any, G any](queue *ArrayQueue[T], initialAccumulator G, f func(item T, accumulator G) G) G { + accumulator := initialAccumulator + for index := 0; index < len(queue.queueData); index += 1 { + accumulator = f(queue.queueData[index], accumulator) + } + + return accumulator +} + +// Iterate over the queue in the reverse direction and apply a function to each item. +// +// It is expected that ReverseApply does *not* update the queue items. +// To modify the queue items, use ReverseMap. +// To accumulate values over the queue, use ReverseFold. +func ReverseApply[T any](queue *ArrayQueue[T], f func(item T)) { + for index := len(queue.queueData) - 1; index >= 0; index -= 1 { + f(queue.queueData[index]) + } +} + +// Iterate over the queue in the reverse direction and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ReverseMap can update the node items by returning the update value. +// If you do not need to modify the queue items, use ReverseApply. +// To accumulate values over the queue, use ReverseFold. +func ReverseMap[T any](queue *ArrayQueue[T], f func(item T) T) { + for index := len(queue.queueData) - 1; index >= 0; index -= 1 { + queue.queueData[index] = f(queue.queueData[index]) + } +} + +// Iterate over the queue and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on ArrayQueue to allow for generic accumulators. +func ReverseFold[T any, G any](queue *ArrayQueue[T], initialAccumulator G, f func(item T, accumulator G) G) G { + accumulator := initialAccumulator + for index := len(queue.queueData) - 1; index >= 0; index -= 1 { + accumulator = f(queue.queueData[index], accumulator) + } + + return accumulator +} diff --git a/queue/ArrayQueue/arrayqueue_apply_test.go b/queue/ArrayQueue/arrayqueue_apply_test.go new file mode 100644 index 0000000..aea8a07 --- /dev/null +++ b/queue/ArrayQueue/arrayqueue_apply_test.go @@ -0,0 +1,122 @@ +package arrayqueue_test + +import ( + "fmt" + "slices" + "testing" + + arrayqueue "github.com/hmcalister/Go-DSA/queue/ArrayQueue" +) + +func TestForwardApply(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + arrayqueue.ForwardApply(queue, func(item string) { concatString += item }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseApply(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + arrayqueue.ReverseApply(queue, func(item string) { concatString += item }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestForwardMap(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + arrayqueue.ForwardMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) +} + +func TestReverseMap(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + arrayqueue.ReverseMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) + +} + +func TestForwardFold(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := arrayqueue.ForwardFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseFold(t *testing.T) { + queue := arrayqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := arrayqueue.ReverseFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} diff --git a/queue/LinkedListQueue/linkedlistqueue.go b/queue/LinkedListQueue/linkedlistqueue.go index 94ecf03..62668a6 100644 --- a/queue/LinkedListQueue/linkedlistqueue.go +++ b/queue/LinkedListQueue/linkedlistqueue.go @@ -81,3 +81,80 @@ func (queue *LinkedListQueue[T]) Remove() (T, error) { return queue.queueData.RemoveAtIndex(0) } + +// ---------------------------------------------------------------------------- +// Apply, Map, and Fold methods +// +// Methods to apply a function across ALL items in a queue. + +// Iterate over the queue in the forward direction and apply a function to each item. +// +// It is expected that ForwardApply does *not* update the queue items. +// To modify the queue items, use ForwardMap. +// To accumulate values over the queue, use ForwardFold. +// +// Internally, this method calls linkedlist.ForwardApply +func ForwardApply[T any](queue *LinkedListQueue[T], f func(item T)) { + linkedlist.ForwardApply(queue.queueData, f) +} + +// Iterate over the queue in the forward direction and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ForwardMap can update the node items by returning the update value. +// If you do not need to modify the queue items, use ForwardApply. +// To accumulate values over the queue, use ForwardFold. +// +// Internally, this method calls linkedlist.ForwardMap +func ForwardMap[T any](queue *LinkedListQueue[T], f func(item T) T) { + linkedlist.ForwardMap(queue.queueData, f) +} + +// Iterate over the queue and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on LinkedListQueue to allow for generic accumulators. +// +// Internally, this method calls linkedlist.ForwardFold +func ForwardFold[T any, G any](queue *LinkedListQueue[T], initialAccumulator G, f func(item T, accumulator G) G) G { + return linkedlist.ForwardFold(queue.queueData, initialAccumulator, f) +} + +// Iterate over the queue in the reverse direction and apply a function to each item. +// +// It is expected that ReverseApply does *not* update the queue items. +// To modify the queue items, use ReverseMap. +// To accumulate values over the queue, use ReverseFold. +// +// Internally, this method calls linkedlist.ReverseApply +func ReverseApply[T any](queue *LinkedListQueue[T], f func(item T)) { + linkedlist.ReverseApply(queue.queueData, f) +} + +// Iterate over the queue in the reverse direction and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ReverseMap can update the node items by returning the update value. +// If you do not need to modify the queue items, use ReverseApply. +// To accumulate values over the queue, use ReverseFold. +// +// Internally, this method calls linkedlist.ReverseMap +func ReverseMap[T any](queue *LinkedListQueue[T], f func(item T) T) { + linkedlist.ReverseMap(queue.queueData, f) +} + +// Iterate over the queue and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on LinkedListQueue to allow for generic accumulators. +// +// Internally, this method calls linkedlist.ReverseFold +func ReverseFold[T any, G any](queue *LinkedListQueue[T], initialAccumulator G, f func(item T, accumulator G) G) G { + return linkedlist.ReverseFold(queue.queueData, initialAccumulator, f) +} diff --git a/queue/LinkedListQueue/linkedlistqueue_apply_test.go b/queue/LinkedListQueue/linkedlistqueue_apply_test.go new file mode 100644 index 0000000..4f54c1f --- /dev/null +++ b/queue/LinkedListQueue/linkedlistqueue_apply_test.go @@ -0,0 +1,122 @@ +package linkedlistqueue_test + +import ( + "fmt" + "slices" + "testing" + + linkedlistqueue "github.com/hmcalister/Go-DSA/queue/LinkedListQueue" +) + +func TestForwardApply(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + linkedlistqueue.ForwardApply(queue, func(item string) { concatString += item }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseApply(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + linkedlistqueue.ReverseApply(queue, func(item string) { concatString += item }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestForwardMap(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + linkedlistqueue.ForwardMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) +} + +func TestReverseMap(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + linkedlistqueue.ReverseMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) + +} + +func TestForwardFold(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := linkedlistqueue.ForwardFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseFold(t *testing.T) { + queue := linkedlistqueue.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := linkedlistqueue.ReverseFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} diff --git a/set/HashSet/hashset.go b/set/HashSet/hashset.go index 1e4cb53..61b44a9 100644 --- a/set/HashSet/hashset.go +++ b/set/HashSet/hashset.go @@ -53,3 +53,35 @@ func (set *HashSet[T]) Items() []T { } return items } + +// Iterate over the items of the hash set and apply a function to each item. +// +// BEWARE: Iteration over a hashset does not guarantee a specific order --- +// you may find elements in any order, not the order they were inserted! +// Ensure your function accounts for this. +// +// To accumulate values over items, use Fold. +func Apply[T comparable](set *HashSet[T], f func(item T)) { + for item := range set.setData { + f(item) + } +} + +// Iterate over set items and apply the function f. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// BEWARE: Iteration over a hashset does not guarantee a specific order --- +// you may find elements in any order, not the order they were inserted! +// Ensure your function accounts for this. This is especially important for +// a fold! +// +// This function is not a method on HashSet to allow for generic accumulators. +func Fold[T comparable, G any](set *HashSet[T], initialAccumulator G, f func(item T, accumulator G) G) G { + accumulator := initialAccumulator + for item := range set.setData { + accumulator = f(item, accumulator) + } + + return accumulator +} diff --git a/set/HashSet/hashset_apply_test.go b/set/HashSet/hashset_apply_test.go new file mode 100644 index 0000000..d0391e6 --- /dev/null +++ b/set/HashSet/hashset_apply_test.go @@ -0,0 +1,47 @@ +package hashset_test + +import ( + "testing" + + hashset "github.com/hmcalister/Go-DSA/set/HashSet" +) + +func TestApply(t *testing.T) { + set := hashset.New[int]() + items := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + for _, item := range items { + set.Add(item) + } + + sum := 0 + hashset.Apply(set, func(item int) { sum += item }) + expectedSum := 0 + for _, item := range items { + expectedSum += item + } + + if sum != expectedSum { + t.Errorf("result (%v) does not match expected result (%v)", sum, expectedSum) + } +} + +func TestFold(t *testing.T) { + set := hashset.New[int]() + items := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + for _, item := range items { + set.Add(item) + } + + sum := hashset.Fold(set, 0, func(item int, accumulator int) int { + return accumulator + item + }) + + expectedSum := 0 + for _, item := range items { + expectedSum += item + } + + if sum != expectedSum { + t.Errorf("result (%v) does not match expected result (%v)", sum, expectedSum) + } +} diff --git a/stack/ArrayStack/arraystack.go b/stack/ArrayStack/arraystack.go index 16f8a81..07c5207 100644 --- a/stack/ArrayStack/arraystack.go +++ b/stack/ArrayStack/arraystack.go @@ -98,3 +98,86 @@ func (stack *ArrayStack[T]) Remove() (T, error) { stack.stackData = stack.stackData[:len(stack.stackData)-1] return item, nil } + +// ---------------------------------------------------------------------------- +// Apply, Map, and Fold methods +// +// Methods to apply a function across ALL items in a stack. + +// Iterate over the stack in the forward direction (bottom to top) and apply a function to each item. +// +// It is expected that ForwardApply does *not* update the stack items. +// To modify the stack items, use ForwardMap. +// To accumulate values over the stack, use ForwardFold. +func ForwardApply[T any](stack *ArrayStack[T], f func(item T)) { + for index := 0; index < len(stack.stackData); index += 1 { + f(stack.stackData[index]) + } +} + +// Iterate over the stack in the forward direction (bottom to top) and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ForwardMap can update the node items by returning the update value. +// If you do not need to modify the stack items, use ForwardApply. +// To accumulate values over the stack, use ForwardFold. +func ForwardMap[T any](stack *ArrayStack[T], f func(item T) T) { + for index := 0; index < len(stack.stackData); index += 1 { + stack.stackData[index] = f(stack.stackData[index]) + } +} + +// Iterate over the stack (bottom to top) and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on ArrayStack to allow for generic accumulators. +func ForwardFold[T any, G any](stack *ArrayStack[T], initialAccumulator G, f func(item T, accumulator G) G) G { + accumulator := initialAccumulator + for index := 0; index < len(stack.stackData); index += 1 { + accumulator = f(stack.stackData[index], accumulator) + } + + return accumulator +} + +// Iterate over the stack in the reverse direction (top to bottom) and apply a function to each item. +// +// It is expected that ReverseApply does *not* update the stack items. +// To modify the stack items, use ReverseMap. +// To accumulate values over the stack, use ReverseFold. +func ReverseApply[T any](stack *ArrayStack[T], f func(item T)) { + for index := len(stack.stackData) - 1; index >= 0; index -= 1 { + f(stack.stackData[index]) + } +} + +// Iterate over the stack in the reverse direction (top to bottom) and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ReverseMap can update the node items by returning the update value. +// If you do not need to modify the stack items, use ReverseApply. +// To accumulate values over the stack, use ReverseFold. +func ReverseMap[T any](stack *ArrayStack[T], f func(item T) T) { + for index := len(stack.stackData) - 1; index >= 0; index -= 1 { + stack.stackData[index] = f(stack.stackData[index]) + } +} + +// Iterate over the stack (top to bottom) and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on ArrayStack to allow for generic accumulators. +func ReverseFold[T any, G any](stack *ArrayStack[T], initialAccumulator G, f func(item T, accumulator G) G) G { + accumulator := initialAccumulator + for index := len(stack.stackData) - 1; index >= 0; index -= 1 { + accumulator = f(stack.stackData[index], accumulator) + } + + return accumulator +} diff --git a/stack/ArrayStack/arraystack_apply_test.go b/stack/ArrayStack/arraystack_apply_test.go new file mode 100644 index 0000000..0fb36df --- /dev/null +++ b/stack/ArrayStack/arraystack_apply_test.go @@ -0,0 +1,122 @@ +package arraystack_test + +import ( + "fmt" + "slices" + "testing" + + arraystack "github.com/hmcalister/Go-DSA/stack/ArrayStack" +) + +func TestForwardApply(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + arraystack.ForwardApply(queue, func(item string) { concatString += item }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseApply(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + arraystack.ReverseApply(queue, func(item string) { concatString += item }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestForwardMap(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + arraystack.ForwardMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) +} + +func TestReverseMap(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + arraystack.ReverseMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) + +} + +func TestForwardFold(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := arraystack.ForwardFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseFold(t *testing.T) { + queue := arraystack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := arraystack.ReverseFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} diff --git a/stack/LinkedListStack/linkedliststack.go b/stack/LinkedListStack/linkedliststack.go index ea92f1d..0fa354d 100644 --- a/stack/LinkedListStack/linkedliststack.go +++ b/stack/LinkedListStack/linkedliststack.go @@ -82,3 +82,80 @@ func (stack *LinkedListStack[T]) Remove() (T, error) { return stack.stackData.Remove() } + +// ---------------------------------------------------------------------------- +// Apply, Map, and Fold methods +// +// Methods to apply a function across ALL items in a stack. + +// Iterate over the stack in the forward direction (bottom to top) and apply a function to each item. +// +// It is expected that ForwardApply does *not* update the stack items. +// To modify the stack items, use ForwardMap. +// To accumulate values over the stack, use ForwardFold. +// +// Internally, this method calls linkedlist.ForwardApply +func ForwardApply[T any](stack *LinkedListStack[T], f func(item T)) { + linkedlist.ForwardApply(stack.stackData, f) +} + +// Iterate over the stack in the forward direction (bottom to top) and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ForwardMap can update the node items by returning the update value. +// If you do not need to modify the stack items, use ForwardApply. +// To accumulate values over the stack, use ForwardFold. +// +// Internally, this method calls linkedlist.ForwardMap +func ForwardMap[T any](stack *LinkedListStack[T], f func(item T) T) { + linkedlist.ForwardMap(stack.stackData, f) +} + +// Iterate over the stack (bottom to top) and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on LinkedListStack to allow for generic accumulators. +// +// Internally, this method calls linkedlist.ForwardFold +func ForwardFold[T any, G any](stack *LinkedListStack[T], initialAccumulator G, f func(item T, accumulator G) G) G { + return linkedlist.ForwardFold(stack.stackData, initialAccumulator, f) +} + +// Iterate over the stack in the reverse direction (top to bottom) and apply a function to each item. +// +// It is expected that ReverseApply does *not* update the stack items. +// To modify the stack items, use ReverseMap. +// To accumulate values over the stack, use ReverseFold. +// +// Internally, this method calls linkedlist.ReverseApply +func ReverseApply[T any](stack *LinkedListStack[T], f func(item T)) { + linkedlist.ReverseApply(stack.stackData, f) +} + +// Iterate over the stack in the reverse direction (top to bottom) and apply a function to each item +// The result of this function is then assigned to the node at each step. +// +// ReverseMap can update the node items by returning the update value. +// If you do not need to modify the stack items, use ReverseApply. +// To accumulate values over the stack, use ReverseFold. +// +// Internally, this method calls linkedlist.ReverseMap +func ReverseMap[T any](stack *LinkedListStack[T], f func(item T) T) { + linkedlist.ReverseMap(stack.stackData, f) +} + +// Iterate over the stack (top to bottom) and apply the function f to it. +// The function f also takes the current value of the accumulator. +// The results of f become the new value of the accumulator at each step. +// +// This function returns the final accumulator. +// +// This function is not a method on LinkedListStack to allow for generic accumulators. +// +// Internally, this method calls linkedlist.ReverseFold +func ReverseFold[T any, G any](stack *LinkedListStack[T], initialAccumulator G, f func(item T, accumulator G) G) G { + return linkedlist.ReverseFold(stack.stackData, initialAccumulator, f) +} diff --git a/stack/LinkedListStack/linkedliststack_apply_test.go b/stack/LinkedListStack/linkedliststack_apply_test.go new file mode 100644 index 0000000..91152e8 --- /dev/null +++ b/stack/LinkedListStack/linkedliststack_apply_test.go @@ -0,0 +1,122 @@ +package linkedliststack_test + +import ( + "fmt" + "slices" + "testing" + + linkedliststack "github.com/hmcalister/Go-DSA/stack/LinkedListStack" +) + +func TestForwardApply(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + linkedliststack.ForwardApply(queue, func(item string) { concatString += item }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseApply(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := "" + linkedliststack.ReverseApply(queue, func(item string) { concatString += item }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestForwardMap(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + linkedliststack.ForwardMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) +} + +func TestReverseMap(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + globalCounter := 0 + linkedliststack.ReverseMap(queue, func(item string) string { + newItem := fmt.Sprintf("%v, %v", item, globalCounter) + globalCounter += 1 + return newItem + }) + +} + +func TestForwardFold(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := linkedliststack.ForwardFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +} + +func TestReverseFold(t *testing.T) { + queue := linkedliststack.New[string]() + items := []string{"a", "b", "c", "d", "e", "f", "g", "h"} + for _, item := range items { + queue.Add(item) + } + + concatString := linkedliststack.ReverseFold(queue, "", func(item string, accumulator string) string { + return accumulator + item + }) + + slices.Reverse(items) + expectedConcatString := "" + for _, item := range items { + expectedConcatString += item + } + + if concatString != expectedConcatString { + t.Errorf("result (%v) does not match expected result (%v)", concatString, expectedConcatString) + } +}