diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f153fcd..76be6cd 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,5 +2,5 @@ blank_issues_enabled: false contact_links: - name: General Question - url: https://github.com/lindsaygelle/hashtable/discussions/categories/q-a + url: https://github.com/lindsaygelle/gomap/discussions/categories/q-a about: Please ask and answer questions as a discussion thread diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ab94f2..0aa1af5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,8 @@ Reporting An Issue/Feature -------------------------- First, check to see if there's an existing issue/pull request for the bug/feature. All issues are at -https://github.com/lindsaygelle/hashtable/issues and pull reqs are at -https://github.com/lindsaygelle/hashtable/pulls. +https://github.com/lindsaygelle/gomap/issues and pull reqs are at +https://github.com/lindsaygelle/gomap/pulls. If there isn't an existing issue there, please file an issue. The ideal report includes: @@ -28,7 +28,7 @@ ideal report includes: - If relevant, including the versions of your: - Go language - - hashtable + - gomap - Optionally of the other dependencies involved - If possible, create a pull request with a (failing) test case diff --git a/Dockerfile b/Dockerfile index 2e51460..084b8c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN wget -q https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz && \ ENV PATH="/usr/local/go/bin:${PATH}" # Set the working directory -WORKDIR /go/src/github.com/lindsaygelle/hashtable +WORKDIR /go/src/github.com/lindsaygelle/gomap # Copy the application source code into the container COPY . . diff --git a/NOTICE b/NOTICE index be83d5d..fde68db 100644 --- a/NOTICE +++ b/NOTICE @@ -1 +1 @@ -hashtable +gomap diff --git a/README.md b/README.md index 511573d..9a9c4f7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# Hashtable -Hashtable is a generic hash table implementation in [Go](https://github.com/golang/go), supporting dynamic key-value pairs of any data type. It offers essential operations like adding, deleting, and checking for key-value pairs, along with advanced functionalities such as iteration, merging, intersection, and conditional mapping. Its flexibility allows seamless manipulation and querying, making it a powerful tool for various applications. +# Gomap +Gomap is a generic map implementation in [Go](https://github.com/golang/go), supporting dynamic key-value pairs of any data type. It offers essential operations like adding, deleting, and checking for key-value pairs, along with advanced functionalities such as iteration, merging, intersection, and conditional mapping. Its flexibility allows seamless manipulation and querying, making it a powerful tool for various applications. -![Hashtable](https://repository-images.githubusercontent.com/192931263/82f0f49f-fe0c-46f7-b52d-d1ded90f4204) +![Gomap](https://repository-images.githubusercontent.com/192931263/82f0f49f-fe0c-46f7-b52d-d1ded90f4204) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/lindsaygelle/hashtable)](https://pkg.go.dev/github.com/lindsaygelle/hashtable) -[![Go Report Card](https://goreportcard.com/badge/github.com/lindsaygelle/hashtable)](https://goreportcard.com/report/github.com/lindsaygelle/hashtable) -[![GitHub release (latest by date)](https://img.shields.io/github/v/release/lindsaygelle/hashtable)](https://github.com/lindsaygelle/hashtable/releases) -[![GitHub](https://img.shields.io/github/license/lindsaygelle/hashtable)](LICENSE.txt) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/lindsaygelle/gomap)](https://pkg.go.dev/github.com/lindsaygelle/gomap) +[![Go Report Card](https://goreportcard.com/badge/github.com/lindsaygelle/gomap)](https://goreportcard.com/report/github.com/lindsaygelle/gomap) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/lindsaygelle/gomap)](https://github.com/lindsaygelle/gomap/releases) +[![GitHub](https://img.shields.io/github/license/lindsaygelle/gomap)](LICENSE.txt) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) ## Features @@ -39,7 +39,7 @@ Retrieve keys or values as slices for easy handling. Quickly check hash table em You can install it in your Go project using `go get`: ```sh -go get github.com/lindsaygelle/hashtable +go get github.com/lindsaygelle/gomap ``` ## Usage @@ -48,29 +48,29 @@ Import the package into your Go code: ```Go import ( - "github.com/lindsaygelle/hashtable" + "github.com/lindsaygelle/gomap" ) ``` ## Methods -Provided methods for `&hashtable.Hashtable[K]V`. +Provided methods for `&gomap.Map[K]V`. ### Add Adds a key-value pair to the hash table and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -myHashtable.Add("key1", 1) -myHashtable.Add("key2", 2) -fmt.Println(myHashtable) // &map[key1:1 key2:2] +myMap := &gomap.Map[string, int]{} +myMap.Add("key1", 1) +myMap.Add("key2", 2) +fmt.Println(myMap) // &map[key1:1 key2:2] ``` ### AddLength Adds a key-value pair to the hash table and returns the new length of the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -length := myHashtable.AddLength("key1", 1) +myMap := &gomap.Map[string, int]{} +length := myMap.AddLength("key1", 1) fmt.Println(length) // 1 ``` @@ -78,29 +78,29 @@ fmt.Println(length) // 1 Adds multiple key-value pairs to the hash table and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -myHashtable.AddMany(map[string]int{"key1": 1, "key2": 2}) -fmt.Println(myHashtable) // &map[key1:1 key2:2] +myMap := &gomap.Map[string, int]{} +myMap.AddMany(map[string]int{"key1": 1, "key2": 2}) +fmt.Println(myMap) // &map[key1:1 key2:2] ``` ### AddManyFunc Adds key-value pairs from a slice of maps to the hash table using a custom function and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} +myMap := &gomap.Map[string, int]{} myMaps := []map[string]int{{"key1": 1}, {"key2": 2}} -myHashtable.AddManyFunc(myMaps, func(i int, key string, value int) bool { +myMap.AddManyFunc(myMaps, func(i int, key string, value int) bool { return true // Add all key-value pairs }) -fmt.Println(myHashtable) // &map[key1:1 key2:2] +fmt.Println(myMap) // &map[key1:1 key2:2] ``` ### AddManyOK Adds multiple key-value pairs to the hash table and returns a slice indicating successful additions. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -results := myHashtable.AddManyOK(map[string]int{"key1": 1, "key2": 2}) +myMap := &gomap.Map[string, int]{} +results := myMap.AddManyOK(map[string]int{"key1": 1, "key2": 2}) fmt.Println(results) // &[true, true] ``` @@ -108,8 +108,8 @@ fmt.Println(results) // &[true, true] Adds a key-value pair to the hash table and returns true if the addition was successful, false otherwise. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -added := myHashtable.AddOK("key1", 1) +myMap := &gomap.Map[string, int]{} +added := myMap.AddOK("key1", 1) fmt.Println(added) // true ``` @@ -117,8 +117,8 @@ fmt.Println(added) // true Checks if the given value is present in the hash table and returns the corresponding key along with a boolean indicating existence. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -key, exists := myHashtable.Contains(2) +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +key, exists := myMap.Contains(2) fmt.Println(key, exists) // key2 true ``` @@ -126,17 +126,17 @@ fmt.Println(key, exists) // key2 true Removes the specified key and its associated value from the hash table and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.Delete("key1") -fmt.Println(myHashtable) // &map[key2:2] +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.Delete("key1") +fmt.Println(myMap) // &map[key2:2] ``` ### DeleteLength Removes the specified key and its associated value from the hash table and returns the new length of the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -length := myHashtable.DeleteLength("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +length := myMap.DeleteLength("key1") fmt.Println(length) // 1 ``` @@ -144,28 +144,28 @@ fmt.Println(length) // 1 Removes multiple keys and their associated values from the hash table and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.DeleteMany("key1", "key2") -fmt.Println(myHashtable) // &map[] +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.DeleteMany("key1", "key2") +fmt.Println(myMap) // &map[] ``` ### DeleteManyFunc Removes key-value pairs from the hash table using a custom function and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.DeleteManyFunc(func(key string, value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.DeleteManyFunc(func(key string, value int) bool { return key == "key1" }) -fmt.Println(myHashtable) // &map[key2:2] +fmt.Println(myMap) // &map[key2:2] ``` ### DeleteManyOK Removes multiple keys and their associated values from the hash table and returns a slice indicating successful deletions. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -results := myHashtable.DeleteManyOK("key1", "key2") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +results := myMap.DeleteManyOK("key1", "key2") fmt.Println(results) // &[true, true] ``` @@ -173,17 +173,17 @@ fmt.Println(results) // &[true, true] Removes multiple values from the hash table and returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.DeleteManyValues(1, 2) -fmt.Println(myHashtable) // &map[] +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.DeleteManyValues(1, 2) +fmt.Println(myMap) // &map[] ``` ### DeleteOK Removes the specified key and its associated value from the hash table and returns true if the deletion was successful, false otherwise. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -deleted := myHashtable.DeleteOK("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +deleted := myMap.DeleteOK("key1") fmt.Println(deleted) // true ``` @@ -191,8 +191,8 @@ fmt.Println(deleted) // true Applies the given function to each key-value pair in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.Each(func(key string, value int) { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.Each(func(key string, value int) { fmt.Println(key, value) }) // Output: @@ -204,8 +204,8 @@ myHashtable.Each(func(key string, value int) { Applies the given function to each key-value pair in the hash table and breaks the iteration if the function returns false. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.EachBreak(func(key string, value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.EachBreak(func(key string, value int) bool { fmt.Println(key, value) return key != "key1" }) @@ -217,8 +217,8 @@ myHashtable.EachBreak(func(key string, value int) bool { Applies the given function to each key in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.EachKey(func(key string) { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.EachKey(func(key string) { fmt.Println(key) }) // Output: @@ -230,8 +230,8 @@ myHashtable.EachKey(func(key string) { Applies the given function to each key in the hash table and breaks the iteration if the function returns false. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.EachKeyBreak(func(key string) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.EachKeyBreak(func(key string) bool { fmt.Println(key) return key != "key1" }) @@ -243,8 +243,8 @@ myHashtable.EachKeyBreak(func(key string) bool { Applies the given function to each value in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.EachValue(func(value int) { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.EachValue(func(value int) { fmt.Println(value) }) // Output: @@ -256,8 +256,8 @@ myHashtable.EachValue(func(value int) { Applies the given function to each value in the hash table and breaks the iteration if the function returns false. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.EachValueBreak(func(value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.EachValueBreak(func(value int) bool { fmt.Println(value) return value != 1 }) @@ -269,8 +269,8 @@ myHashtable.EachValueBreak(func(value int) bool { Empties the current hash table and inserts its content into another hash table. It returns the updated destination hash table. ```Go -source := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -destination := &hashtable.Hashtable[string, int]{"key3": 3} +source := &gomap.Map[string, int]{"key1": 1, "key2": 2} +destination := &gomap.Map[string, int]{"key3": 3} destination = source.EmptyInto(destination) fmt.Println(destination) // &map[key1:1 key2:2] ``` @@ -279,9 +279,9 @@ fmt.Println(destination) // &map[key1:1 key2:2] Checks if the current hash table is equal to another hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -isEqual := myHashtable1.Equal(myHashtable2) +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +isEqual := myMap1.Equal(myMap2) fmt.Println(isEqual) // true ``` @@ -289,9 +289,9 @@ fmt.Println(isEqual) // true Checks if the current hash table is equal to another hash table using a custom comparison function. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 4} -isEqual := myHashtable1.EqualFunc(myHashtable2, func(a int, b int) bool { +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key1": 1, "key2": 4} +isEqual := myMap1.EqualFunc(myMap2, func(a int, b int) bool { return a == b || a%2 == 0 && b%2 == 0 }) fmt.Println(isEqual) // true (custom comparison allows for even values) @@ -301,9 +301,9 @@ fmt.Println(isEqual) // true (custom comparison allows for even values) Checks if the current hash table is equal in length to another hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -isEqualLength := myHashtable1.EqualLength(myHashtable2) +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +isEqualLength := myMap1.EqualLength(myMap2) fmt.Println(isEqualLength) // false (different lengths) ``` @@ -311,8 +311,8 @@ fmt.Println(isEqualLength) // false (different lengths) Retrieves the value associated with the given key from the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -value := myHashtable.Fetch("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +value := myMap.Fetch("key1") fmt.Println(value) // 1 ``` @@ -320,8 +320,8 @@ fmt.Println(value) // 1 Applies the given function to each key-value pair in the hash table and retains pairs for which the function returns true. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -filteredHashtable := myHashtable.Filter(func(key string, value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +filteredHashtable := myMap.Filter(func(key string, value int) bool { return value%2 == 0 }) fmt.Println(filteredHashtable) // &map[key2:2] @@ -331,8 +331,8 @@ fmt.Println(filteredHashtable) // &map[key2:2] Retrieves the value associated with the given key from the hash table and returns it along with a boolean indicating existence. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -value, exists := myHashtable.Get("key2") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +value, exists := myMap.Get("key2") fmt.Println(value, exists) // 2 true ``` @@ -340,8 +340,8 @@ fmt.Println(value, exists) // 2 true Retrieves values associated with multiple keys from the hash table and returns them in a slice. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -values := myHashtable.GetMany("key1", "key3") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +values := myMap.GetMany("key1", "key3") fmt.Println(values) // &[1 3] ``` @@ -349,8 +349,8 @@ fmt.Println(values) // &[1 3] Checks if the given key is present in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -exists := myHashtable.Has("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +exists := myMap.Has("key1") fmt.Println(exists) // true ``` @@ -358,8 +358,8 @@ fmt.Println(exists) // true Checks if multiple keys are present in the hash table and returns a slice indicating their existence. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -exists := myHashtable.HasMany("key1", "key4") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +exists := myMap.HasMany("key1", "key4") fmt.Println(exists) // &[true false] ``` @@ -367,9 +367,9 @@ fmt.Println(exists) // &[true false] Returns a new hash table containing key-value pairs that are present in both the current and another hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key1": 1, "key3": 3} -intersection := myHashtable1.Intersection(myHashtable2) +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key1": 1, "key3": 3} +intersection := myMap1.Intersection(myMap2) fmt.Println(intersection) // &map[key1:1] ``` @@ -377,9 +377,9 @@ fmt.Println(intersection) // &map[key1:1] Returns a new hash table containing key-value pairs that are present in both the current and another hash table, determined by a custom function. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key1": 1, "key3": 3} -intersection := myHashtable1.IntersectionFunc(myHashtable2, func(key string, a int, b int) bool { +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key1": 1, "key3": 3} +intersection := myMap1.IntersectionFunc(myMap2, func(key string, a int, b int) bool { return a == b }) fmt.Println(intersection) // &map[key1:1] @@ -389,8 +389,8 @@ fmt.Println(intersection) // &map[key1:1] Checks if the hash table is empty. ```Go -myHashtable := &hashtable.Hashtable[string, int]{} -isEmpty := myHashtable.IsEmpty() +myMap := &gomap.Map[string, int]{} +isEmpty := myMap.IsEmpty() fmt.Println(isEmpty) // true ``` @@ -398,8 +398,8 @@ fmt.Println(isEmpty) // true Checks if the hash table contains key-value pairs. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -isPopulated := myHashtable.IsPopulated() +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +isPopulated := myMap.IsPopulated() fmt.Println(isPopulated) // true ``` @@ -407,8 +407,8 @@ fmt.Println(isPopulated) // true Returns a slice containing all keys in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -keys := myHashtable.Keys() +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +keys := myMap.Keys() fmt.Println(keys) // &["key1" "key2"] ``` @@ -416,8 +416,8 @@ fmt.Println(keys) // &["key1" "key2"] Returns a slice containing keys that satisfy the given function. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -keys := myHashtable.KeysFunc(func(key string) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +keys := myMap.KeysFunc(func(key string) bool { return key != "key3" }) fmt.Println(keys) // &["key1" "key2"] @@ -427,8 +427,8 @@ fmt.Println(keys) // &["key1" "key2"] Returns the number of key-value pairs in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -length := myHashtable.Length() +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +length := myMap.Length() fmt.Println(length) // 2 ``` @@ -436,31 +436,31 @@ fmt.Println(length) // 2 Applies the given function to each key-value pair in the hash table and replaces the value with the result of the function. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.Map(func(key string, value int) int { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.Map(func(key string, value int) int { return value * 2 }) -fmt.Println(myHashtable) // &map[key1:2 key2:4] +fmt.Println(myMap) // &map[key1:2 key2:4] ``` ### MapBreak Applies the given function to each key-value pair in the hash table and replaces the value with the result of the function. It breaks the iteration if the function returns false. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable.MapBreak(func(key string, value int) (int, bool) { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap.MapBreak(func(key string, value int) (int, bool) { return value * 2, key != "key2" }) -fmt.Println(myHashtable) // &map[key1:2 key2:2] +fmt.Println(myMap) // &map[key1:2 key2:2] ``` ### Merge Merges the current hash table with another hash table and returns the updated hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key2": 3, "key3": 4} -mergedHashtable := myHashtable1.Merge(myHashtable2) +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key2": 3, "key3": 4} +mergedHashtable := myMap1.Merge(myMap2) fmt.Println(mergedHashtable) // &map[key1:1 key2:3 key3:4] ``` @@ -468,9 +468,9 @@ fmt.Println(mergedHashtable) // &map[key1:1 key2:3 key3:4] Merges the current hash table with another hash table using a custom function and returns the updated hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key2": 3, "key3": 4} -mergedHashtable := myHashtable1.MergeFunc(myHashtable2, func(key string, value1 int, value2 int) bool { +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key2": 3, "key3": 4} +mergedHashtable := myMap1.MergeFunc(myMap2, func(key string, value1 int, value2 int) bool { return value1 > value2 }) fmt.Println(mergedHashtable) // &map[key1:1 key2:2 key3:4] @@ -480,9 +480,9 @@ fmt.Println(mergedHashtable) // &map[key1:1 key2:2 key3:4] Merges the current hash table with multiple other hash tables and returns the updated hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key2": 3, "key3": 4} -mergedHashtable := myHashtable1.MergeMany(myHashtable2, &hashtable.Hashtable[string, int]{"key4": 5}) +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key2": 3, "key3": 4} +mergedHashtable := myMap1.MergeMany(myMap2, &gomap.Map[string, int]{"key4": 5}) fmt.Println(mergedHashtable) // &map[key1:1 key2:3 key3:4 key4:5] ``` @@ -490,9 +490,9 @@ fmt.Println(mergedHashtable) // &map[key1:1 key2:3 key3:4 key4:5] Merges the current hash table with multiple other hash tables using a custom function and returns the updated hash table. ```Go -myHashtable1 := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -myHashtable2 := &hashtable.Hashtable[string, int]{"key2": 3, "key3": 4} -mergedHashtable := myHashtable1.MergeManyFunc([]*hashtable.Hashtable[string, int]{myHashtable2}, func(i int, key string, value int) bool { +myMap1 := &gomap.Map[string, int]{"key1": 1, "key2": 2} +myMap2 := &gomap.Map[string, int]{"key2": 3, "key3": 4} +mergedHashtable := myMap1.MergeManyFunc([]*gomap.Map[string, int]{myMap2}, func(i int, key string, value int) bool { return key != "key3" }) fmt.Println(mergedHashtable) // &map[key1:1 key2:3] @@ -502,8 +502,8 @@ fmt.Println(mergedHashtable) // &map[key1:1 key2:3] Checks if the given key is not present in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -notPresent := myHashtable.Not("key3") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +notPresent := myMap.Not("key3") fmt.Println(notPresent) // true ``` @@ -511,8 +511,8 @@ fmt.Println(notPresent) // true Checks if multiple keys are not present in the hash table and returns a slice indicating their absence. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -absentKeys := myHashtable.NotMany("key3", "key4") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +absentKeys := myMap.NotMany("key3", "key4") fmt.Println(absentKeys) // &[true true] ``` @@ -520,64 +520,64 @@ fmt.Println(absentKeys) // &[true true] Removes the specified key and its associated value from the hash table and returns the value. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -value := myHashtable.Pop("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +value := myMap.Pop("key1") fmt.Println(value) // 1 -fmt.Println(myHashtable) // &map[key2:2] +fmt.Println(myMap) // &map[key2:2] ``` ### PopMany Removes multiple keys and their associated values from the hash table and returns the values in a slice. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -values := myHashtable.PopMany("key1", "key3") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +values := myMap.PopMany("key1", "key3") fmt.Println(values) // &[1 3] -fmt.Println(myHashtable) // &map[key2:2] +fmt.Println(myMap) // &map[key2:2] ``` ### PopManyFunc Removes key-value pairs from the hash table using a custom function and returns the values in a slice. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -values := myHashtable.PopManyFunc(func(key string, value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +values := myMap.PopManyFunc(func(key string, value int) bool { return key != "key2" }) fmt.Println(values) // &[1 3] -fmt.Println(myHashtable) // &map[key2:2] +fmt.Println(myMap) // &map[key2:2] ``` ### PopOK Removes the specified key and its associated value from the hash table and returns the value along with a boolean indicating existence. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -value, exists := myHashtable.PopOK("key1") +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +value, exists := myMap.PopOK("key1") fmt.Println(value, exists) // 1 true -fmt.Println(myHashtable) // &map[key2:2] +fmt.Println(myMap) // &map[key2:2] ``` ### ReplaceMany Applies the given function to each key-value pair in the hash table and replaces the value if the function returns true. It returns the updated hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -myHashtable.ReplaceMany(func(key string, value int) (int, bool) { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +myMap.ReplaceMany(func(key string, value int) (int, bool) { if key == "key2" { return value * 2, true } return value, false }) -fmt.Println(myHashtable) // &map[key1:1 key2:4 key3:3] +fmt.Println(myMap) // &map[key1:1 key2:4 key3:3] ``` ### TakeFrom Empties the current hash table and inserts its content into another hash table. It returns the updated destination hash table. ```Go -source := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -destination := &hashtable.Hashtable[string, int]{"key3": 3} +source := &gomap.Map[string, int]{"key1": 1, "key2": 2} +destination := &gomap.Map[string, int]{"key3": 3} destination = source.TakeFrom(destination) fmt.Println(destination) // &map[key1:1 key2:2] ``` @@ -586,8 +586,8 @@ fmt.Println(destination) // &map[key1:1 key2:2] Returns a slice containing all values in the hash table. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2} -values := myHashtable.Values() +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2} +values := myMap.Values() fmt.Println(values) // &[1 2] ``` @@ -595,8 +595,8 @@ fmt.Println(values) // &[1 2] Returns a slice containing values that satisfy the given function. ```Go -myHashtable := &hashtable.Hashtable[string, int]{"key1": 1, "key2": 2, "key3": 3} -values := myHashtable.ValuesFunc(func(key string, value int) bool { +myMap := &gomap.Map[string, int]{"key1": 1, "key2": 2, "key3": 3} +values := myMap.ValuesFunc(func(key string, value int) bool { return value%2 == 0 }) fmt.Println(values) // &[2] @@ -612,7 +612,7 @@ package main import ( "fmt" - "github.com/lindsaygelle/hashtable" // Import the hashtable package + "github.com/lindsaygelle/gomap" // Import the gomap package ) type Animal struct { @@ -625,52 +625,52 @@ func main() { // Create a new Animal fluffy := Animal{Age: 1, Name: "Fluffy", Species: "Cat"} - // Create a new Hashtable of Animals - animals := &hashtable.Hashtable[string, Animal]{} + // Create a new Map of Animals + animals := &gomap.Map[string, Animal]{} - // Add an Animal to the Hashtable using Add function + // Add an Animal to the Map using Add function animals.Add(fluffy.Name, fluffy) - // Check if the Hashtable contains a specific key + // Check if the Map contains a specific key if animals.Has("Fluffy") { - fmt.Println("Fluffy is in the Hashtable!") + fmt.Println("Fluffy is in the Map!") } // Get the value associated with a specific key if animal, exists := animals.Get("Fluffy"); exists { - fmt.Println("Found Fluffy in the Hashtable:", animal) + fmt.Println("Found Fluffy in the Map:", animal) } // Update the age of Fluffy updatedFluffy := Animal{Age: 2, Name: "Fluffy", Species: "Cat"} animals.Add("Fluffy", updatedFluffy) - // Delete an entry from the Hashtable + // Delete an entry from the Map animals.Delete("Fluffy") // Add more animals after deleting Fluffy animals.Add("Buddy", Animal{Age: 3, Name: "Buddy", Species: "Dog"}) animals.Add("Whiskers", Animal{Age: 2, Name: "Whiskers", Species: "Rabbit"}) - // Iterate over the Hashtable and print each key-value pair + // Iterate over the Map and print each key-value pair animals.Each(func(key string, value Animal) { fmt.Println("Key:", key, "Value:", value) }) - // Check if the Hashtable is empty + // Check if the Map is empty if animals.IsEmpty() { - fmt.Println("Hashtable is empty.") + fmt.Println("Map is empty.") } else { - fmt.Println("Hashtable is not empty.") + fmt.Println("Map is not empty.") } - // Get all keys from the Hashtable + // Get all keys from the Map keys := animals.Keys().Slice() - fmt.Println("Keys in Hashtable:", keys) + fmt.Println("Keys in Map:", keys) - // Get all values from the Hashtable + // Get all values from the Map values := animals.Values().Slice() - fmt.Println("Values in Hashtable:", values) + fmt.Println("Values in Map:", values) } ``` @@ -683,7 +683,7 @@ package main import ( "fmt" - "github.com/lindsaygelle/hashtable" // Import the hashtable package + "github.com/lindsaygelle/gomap" // Import the gomap package ) type Animal struct { @@ -693,18 +693,18 @@ type Animal struct { } func main() { - // Create a new Hashtable of Animals and add animals using method chaining - animals := &hashtable.Hashtable[string, Animal]{}. + // Create a new Map of Animals and add animals using method chaining + animals := &gomap.Map[string, Animal]{}. Add("Fluffy", Animal{Age: 1, Name: "Fluffy", Species: "Cat"}). Add("Buddy", Animal{Age: 3, Name: "Buddy", Species: "Dog"}). Add("Whiskers", Animal{Age: 2, Name: "Whiskers", Species: "Rabbit"}) - // Print the number of animals in the Hashtable using the Length() method - fmt.Println("Number of animals in the Hashtable:", animals.Length()) + // Print the number of animals in the Map using the Length() method + fmt.Println("Number of animals in the Map:", animals.Length()) - // Check if a specific animal is in the Hashtable using method chaining + // Check if a specific animal is in the Map using method chaining if exists := animals.Has("Fluffy"); exists { - fmt.Println("Fluffy is in the Hashtable!") + fmt.Println("Fluffy is in the Map!") } // Retrieve and print the age of a specific animal using method chaining @@ -712,7 +712,7 @@ func main() { fmt.Println("Buddy's age is:", age.Age) } - // Iterate over the Hashtable and print each key-value pair using method chaining + // Iterate over the Map and print each key-value pair using method chaining animals.Each(func(key string, value Animal) { fmt.Println("Key:", key, "Value:", value) }) @@ -741,13 +741,13 @@ A [Dockerfile](./Dockerfile) is provided for individuals that prefer containeriz ### Building Building the Docker container: ```sh -docker build . -t hashtable +docker build . -t gomap ``` ### Running Developing and running Go within the Docker container: ```sh -docker run -it --rm --name hashtable hashtable +docker run -it --rm --name gomap gomap ``` ## Docker Compose @@ -759,13 +759,13 @@ docker-compose up -d ``` ## Contributing -We warmly welcome contributions to Hashtable. Whether you have innovative ideas, bug reports, or enhancements in mind, please share them with us by submitting GitHub issues or creating pull requests. For substantial contributions, it's a good practice to start a discussion by creating an issue to ensure alignment with the project's goals and direction. Refer to the [CONTRIBUTING](./CONTRIBUTING.md) file for comprehensive details. +We warmly welcome contributions to Map. Whether you have innovative ideas, bug reports, or enhancements in mind, please share them with us by submitting GitHub issues or creating pull requests. For substantial contributions, it's a good practice to start a discussion by creating an issue to ensure alignment with the project's goals and direction. Refer to the [CONTRIBUTING](./CONTRIBUTING.md) file for comprehensive details. ## Branching For a smooth collaboration experience, we have established branch naming conventions and guidelines. Please consult the [BRANCH_NAMING_CONVENTION](./BRANCH_NAMING_CONVENTION.md) document for comprehensive information and best practices. ## License -Hashtable is released under the MIT License, granting you the freedom to use, modify, and distribute the code within this repository in accordance with the terms of the license. For additional information, please review the [LICENSE](./LICENSE) file. +Map is released under the MIT License, granting you the freedom to use, modify, and distribute the code within this repository in accordance with the terms of the license. For additional information, please review the [LICENSE](./LICENSE) file. ## Security If you discover a security vulnerability within this project, please consult the [SECURITY](./SECURITY.md) document for information and next steps. diff --git a/benchmark_test.go b/benchmark_test.go new file mode 100644 index 0000000..a9ba39f --- /dev/null +++ b/benchmark_test.go @@ -0,0 +1,120 @@ +package gomap_test + +import ( + "testing" + + "github.com/lindsaygelle/gomap" +) + +func BenchmarkAdd(b *testing.B) { + newMap := &gomap.Map[int, int]{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Add(i, i) + } +} + +func BenchmarkDelete(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Delete(i % 1000) + } +} + +func BenchmarkFilter(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Filter(func(key int, value int) bool { + return value%2 == 0 + }) + } +} + +func BenchmarkGet(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Get(i % 1000) + } +} + +func BenchmarkIntersection(b *testing.B) { + newMap1 := &gomap.Map[int, int]{} + newMap2 := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap1.Add(i, i) + newMap2.Add(i+500, i+500) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap1.Intersection(newMap2) + } +} + +func BenchmarkLength(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Length() + } +} + +func BenchmarkMap(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Map(func(key int, value int) int { + return value * 2 + }) + } +} + +func BenchmarkMerge(b *testing.B) { + newMap1 := &gomap.Map[int, int]{} + newMap2 := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap1.Add(i, i) + newMap2.Add(i+1000, i+1000) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap1.Merge(newMap2) + } +} + +func BenchmarkValues(b *testing.B) { + newMap := &gomap.Map[int, int]{} + for i := 0; i < 1000; i++ { + newMap.Add(i, i) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + newMap.Values() + } +} diff --git a/doc.go b/doc.go index 85c268d..542520a 100644 --- a/doc.go +++ b/doc.go @@ -1,2 +1,2 @@ -// Package hashtable provides a generic hash table implementation that maps keys of type K to values of type V. -package hashtable +// Package gomap provides a generic map implementation that maps keys of type K to values of type V. +package gomap diff --git a/docker-compose.yml b/docker-compose.yml index 9b74356..ab06053 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ version: "3" services: - hashtable: + gomap: build: context: . dockerfile: Dockerfile - container_name: hashtable - image: hashtable:latest + container_name: gomap + image: gomap:latest diff --git a/go.mod b/go.mod index 3ec58d6..b016bf9 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/lindsaygelle/hashtable +module github.com/lindsaygelle/gomap go 1.21 diff --git a/gomap.go b/gomap.go new file mode 100644 index 0000000..fd5bb61 --- /dev/null +++ b/gomap.go @@ -0,0 +1,1028 @@ +package gomap + +import ( + "reflect" + + "github.com/lindsaygelle/slice" +) + +// Map represents a generic map that maps keys of type K to values of type V. +type Map[K comparable, V any] map[K]V + +// Add inserts a new key-value pair into the map 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 map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// newMap.Add("banana", 10) // Updates the value for the key "banana" to 10 +// fmt.Println(newMap) // &map[apple:5 banana:10 cherry:8] +func (gomap *Map[K, V]) Add(key K, value V) *Map[K, V] { + (*gomap)[key] = value + return gomap +} + +// AddLength inserts a new key-value pair into the map 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 map. +// It then returns the current length of the map after the addition or update operation. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// length := newMap.AddLength("apple", 5) // Adds "apple" with value 5, returns the length of the map (1 in this case) +// length = newMap.AddLength("apple", 10) // Updates the value for "apple" to 10, returns the length of the map (1) +// length = newMap.AddLength("banana", 3) // Adds "banana" with value 3, returns the length of the map (2) +func (gomap *Map[K, V]) AddLength(key K, value V) int { + return gomap.Add(key, value).Length() +} + +// AddMany inserts multiple key-value pairs into the map. It accepts a variadic number of maps, where each map contains +// key-value pairs to be added to the map. If a key already exists in the gomap, the corresponding value is updated +// with the new value from the input maps. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.AddMany(map[string]int{"orange": 7, "grape": 4}, map[string]int{"kiwi": 6, "pear": 9}) +// fmt.Println(newMap) // &map[orange:7 grape:4 kiwi:6 pear:9] +func (gomap *Map[K, V]) AddMany(values ...map[K]V) *Map[K, V] { + for _, item := range values { + for key, value := range item { + gomap.Add(key, value) + } + } + return gomap +} + +// AddManyFunc inserts key-value pairs into the map 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 map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.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(newMap) // &map[apple:5 banana:10] +func (gomap *Map[K, V]) AddManyFunc(values []map[K]V, fn func(i int, key K, value V) bool) *Map[K, V] { + for i, item := range values { + for key, value := range item { + if fn(i, key, value) { + gomap.Add(key, value) + } + } + } + return gomap +} + +// AddManyOK inserts multiple key-value pairs into the map 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 map. +// For each key-value pair, it checks if the key already exists in the map. 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. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// results := newMap.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 (gomap *Map[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 { + ok := gomap.Not(key) + if ok { + gomap.Add(key, value) + } + successfulInsertions.Append(ok) + } + } + return &successfulInsertions +} + +// AddOK inserts a new key-value pair into the map only if the key does not already exist in the map. +// 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 gomap, +// and true is returned to indicate a successful insertion. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// +// // Attempt to add key-value pairs. +// added := newMap.AddOK("apple", 5) // added is true, "apple" is added with value 5. +// reAdded := newMap.AddOK("apple", 10) // reAdded is false, "apple" already exists with value 5, no change is made. +// addedNew := newMap.AddOK("banana", 3) // addedNew is true, "banana" is added with value 3. +func (gomap *Map[K, V]) AddOK(key K, value V) bool { + ok := !gomap.Has(key) + if ok { + gomap.Add(key, value) + } + return ok +} + +// Contains checks if the given value is present in the map and returns the first key-value pair that matches the value. +// It takes a value as input and returns the key and a boolean indicating whether the value is found in the map. +// If the value is found, it returns the corresponding key and true. If the value is not found, it returns the zero value for the key type and false. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// key, found := newMap.Contains(5) // Checks if value 5 is in the gomap, returns the key ("apple" for example) and true if found, or ("", false) if not found +func (gomap *Map[K, V]) Contains(value V) (K, bool) { + var k K + var ok bool + gomap.EachBreak(func(key K, v V) bool { + ok = reflect.DeepEqual(v, value) + if ok { + k = key + } + return !ok + }) + return k, ok +} + +// Delete removes a key-value pair from the map based on the provided key. If the key exists in the gomap, +// it is deleted, and the modified map is returned. If the key is not found, the map remains unchanged. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Delete the key-value pair with the key "apple". +// newMap.Delete("apple") +// fmt.Println(newMap) // &map[banana:3] +func (gomap *Map[K, V]) Delete(key K) *Map[K, V] { + delete(*gomap, key) + return gomap +} + +// DeleteLength removes a key-value pair from the map based on the provided key. If the key exists in the gomap, +// it is deleted, and the current length of the map after the deletion is returned. If the key is not found, +// the map remains unchanged, and the current length is returned. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Delete the key-value pair with the key "apple" and get the updated length of the map. +// length := newMap.DeleteLength("apple") +// // After deletion, the length of the map is 1. +// // The current length returned: 1 +func (gomap *Map[K, V]) DeleteLength(key K) int { + return gomap.Delete(key).Length() +} + +// DeleteMany removes multiple key-value pairs from the map based on the provided keys. If a key exists in the gomap, +// it is deleted. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Delete key-value pairs with the keys "apple" and "banana". +// newMap.DeleteMany("apple", "banana") +// fmt.Println(newMap) // &map[] +func (gomap *Map[K, V]) DeleteMany(keys ...K) *Map[K, V] { + for _, key := range keys { + gomap.Delete(key) + } + return gomap +} + +// DeleteFunc removes key-value pairs from the map 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 map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Delete key-value pairs where the value is less than 4. +// newMap.DeleteFunc(func(key string, value int) bool { +// return value < 4 +// }) +// fmt.Println(newMap) // &map[apple:5] +func (gomap *Map[K, V]) DeleteManyFunc(fn func(key K, value V) bool) *Map[K, V] { + for key, value := range *gomap { + if fn(key, value) { + gomap.Delete(key) + } + } + return gomap +} + +// DeleteManyOK removes multiple key-value pairs from the map based on the provided keys. If a key exists in the gomap, +// 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. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Attempt to delete key-value pairs with the keys "apple" and "orange". +// results := newMap.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 (gomap *Map[K, V]) DeleteManyOK(keys ...K) *slice.Slice[bool] { + deletetions := make(slice.Slice[bool], 0) + for _, key := range keys { + deletetions.Append(gomap.DeleteOK(key)) + } + return &deletetions +} + +// DeleteManyValues removes key-value pairs from the map based on the provided values. If a value exists in the gomap, +// the corresponding key-value pair is deleted. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Delete key-value pairs with the values 5 and 10. +// newMap.DeleteManyValues(5, 10) +// // Map after deletion: {"banana": 3} +func (gomap *Map[K, V]) DeleteManyValues(values ...V) *Map[K, V] { + for key, value := range *gomap { + for _, v := range values { + if reflect.DeepEqual(v, value) { + gomap.Delete(key) + } + } + } + return gomap +} + +// DeleteOK removes a key-value pair from the map based on the provided key. If the key exists in the gomap, +// it is deleted, and true is returned to indicate a successful deletion. If the key is not found, the map remains unchanged, +// and false is returned to indicate that the deletion failed. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Attempt to delete the key-value pair with the key "apple". +// success := newMap.DeleteOK("apple") +// // After deletion, the key "apple" is not found in the map. +// // Deletion success: true +// +// // Attempt to delete a non-existing key. +// success = newMap.DeleteOK("orange") +// // The key "orange" does not exist in the map. +// // Deletion failed: false +func (gomap *Map[K, V]) DeleteOK(key K) bool { + return !gomap.Delete(key).Has(key) +} + +// Each iterates over the key-value pairs in the map and applies a function to each pair. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Iterate over the map and print all key-value pairs. +// newMap.Each(func(key string, value int) { +// fmt.Println(key, value) +// }) +// // Output: "apple 5", "banana 3", "cherry 8" +func (gomap *Map[K, V]) Each(fn func(key K, value V)) *Map[K, V] { + return gomap.EachBreak(func(key K, value V) bool { + fn(key, value) + return true + }) +} + +// EachBreak applies the provided function to each key-value pair in the map. The function is applied to key-value pairs +// in the map until the provided function returns false. If the function returns false for any key-value pair, +// the iteration breaks early. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Function to print key-value pairs until finding "banana". +// stopPrinting := newMap.EachBreak(func(key string, value int) bool { +// fmt.Println(key, value) +// return key != "banana" // Continue printing until "banana" is encountered. +// }) +// // Output: "apple 5", "banana 3" +func (gomap *Map[K, V]) EachBreak(fn func(key K, value V) bool) *Map[K, V] { + for key, value := range *gomap { + if !fn(key, value) { + break + } + } + return gomap +} + +// EachKey iterates over the keys in the map and applies a function to each key. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Iterate over the map and print each key. +// newMap.EachKey(func(key string) { +// fmt.Println(key) +// }) +// // Output: "apple", "banana", "cherry" +func (gomap *Map[K, V]) EachKey(fn func(key K)) *Map[K, V] { + return gomap.Each(func(key K, _ V) { + fn(key) + }) +} + +// EachKeyBreak iterates over the keys in the map and applies a function to each key. It allows breaking the iteration early if the provided function returns false. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Iterate over the map keys, print them, and break when "banana" is encountered. +// newMap.EachKeyBreak(func(key string) bool { +// fmt.Println(key) +// return key != "banana" +// }) +// // Output: "apple", "banana" +func (gomap *Map[K, V]) EachKeyBreak(fn func(key K) bool) *Map[K, V] { + return gomap.EachBreak(func(key K, _ V) bool { + return fn(key) + }) +} + +// EachValue iterates over the values in the map and applies a function to each value. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Iterate over the map values and print them. +// newMap.EachValue(func(value int) { +// fmt.Println(value) +// }) +// // Output: 5, 3, 8 +func (gomap *Map[K, V]) EachValue(fn func(value V)) *Map[K, V] { + return gomap.Each(func(_ K, value V) { + fn(value) + }) +} + +// EachValueBreak iterates over the values in the map and applies a function to each value until the function returns false. +// If the provided function returns false, the iteration breaks early. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Iterate over the map values and process them until the value is 3. +// newMap.EachValueBreak(func(value int) bool { +// fmt.Println(value) +// return value != 3 +// }) +// // Output: 5, 3 +func (gomap *Map[K, V]) EachValueBreak(fn func(value V) bool) *Map[K, V] { + return gomap.EachBreak(func(_ K, value V) bool { + return fn(value) + }) +} + +// EmptyInto transfers all key-value pairs from the current map into another gomap, emptying the current map. +// It takes another map as input and adds all key-value pairs from the current map to the other map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// +// newMap1.EmptyInto(newMap2) // Transfers "apple": 5 from newMap1 to newMap2, leaving newMap1 empty +func (gomap *Map[K, V]) EmptyInto(other *Map[K, V]) *Map[K, V] { + gomap.Each(func(key K, value V) { + other.Add(key, gomap.Pop(key)) + }) + return gomap +} + +// Equal checks if the current map is equal to another map by comparing the key-value pairs directly using reflect.DeepEqual. +// It takes another map as input and returns true if the two hashtables are equal, false otherwise. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// newMap1.Add("orange", 10) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("apple", 5) +// newMap2.Add("orange", 10) +// +// equal := newMap1.Equal(newMap2) // Returns true because newMap1 and newMap2 have the same key-value pairs +func (gomap *Map[K, V]) Equal(other *Map[K, V]) bool { + return gomap.EqualFunc(other, func(a, b V) bool { + return reflect.DeepEqual(a, b) + }) +} + +// EqualFunc checks if the current map is equal to another map based on a provided comparison function. +// It takes another map and a comparison function as input and returns true if the two hashtables are equal according to the function. +// The comparison function takes two values as input and returns true if they are considered equal, false otherwise. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// newMap1.Add("orange", 10) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("apple", 5) +// newMap2.Add("orange", 11) +// +// equal := newMap1.EqualFunc(newMap2, func(a, b int) bool { +// return math.Abs(float64(a - b)) <= 1 +// }) // Returns true because the values for "orange" (10 and 11) have a difference of 1, within the allowed range +func (gomap *Map[K, V]) EqualFunc(other *Map[K, V], fn func(a V, b V) bool) bool { + if !gomap.EqualLength(other) { + return false + } + for key, value := range *gomap { + v, ok := other.Get(key) + if !ok || !fn(value, v) { + return false + } + } + return true +} + +// EqualLength checks if the current map has the same length as another map. +// It takes another map as input and returns true if the two hashtables have the same length, false otherwise. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// newMap1.Add("orange", 10) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("apple", 5) +// +// equalLength := newMap1.EqualLength(newMap2) // Returns false because newMap1 has a length of 2, while newMap2 has a length of 1 +func (gomap *Map[K, V]) EqualLength(other *Map[K, V]) bool { + return gomap.Length() == other.Length() +} + +// Fetch retrieves the value associated with the given key from the map. +// It returns the value if the key is found in the gomap, and the zero value for the value type if the key is not present. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// value := newMap.Fetch("apple") // Returns 5, the value associated with the key "apple" +// value = newMap.Fetch("orange") // Returns 0 because "orange" is not in the gomap +func (gomap *Map[K, V]) Fetch(key K) V { + value, _ := gomap.Get(key) + return value +} + +// Get retrieves the value associated with the provided key from the map. +// If the key exists, it returns the associated value and true. Otherwise, it returns the zero value for the value type and false. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// value, exists := newMap.Get("apple") // 5, true +// value, exists = newMap.Get("orange") // 0, false +func (gomap *Map[K, V]) Get(key K) (V, bool) { + value, ok := (*gomap)[key] + return value, ok +} + +// Filter applies the given function to each key-value pair in the map and returns a new gomap +// containing only the key-value pairs for which the function returns true. The original map is not modified. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Create a new map containing key-value pairs where the value is greater than 4. +// filteredMap := newMap.Filter(func(key string, value int) bool { +// return value > 4 +// }) +func (gomap *Map[K, V]) Filter(fn func(key K, value V) bool) *Map[K, V] { + other := make(Map[K, V], 0) + gomap.Each(func(key K, value V) { + if fn(key, value) { + other.Add(key, value) + } + }) + return &other +} + +// GetMany retrieves the values associated with the provided keys from the map. It accepts a variadic number of keys, +// and returns a slice containing the values corresponding to the keys found in the map. If a key is not found in the gomap, +// the corresponding position in the returned slice will be the zero value for the value type. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Get values for specific keys. +// values := newMap.GetMany("apple", "banana", "orange") // The resulting values slice: {5, 3} +func (gomap *Map[K, V]) GetMany(keys ...K) *slice.Slice[V] { + values := &slice.Slice[V]{} + for _, key := range keys { + if value, ok := gomap.Get(key); ok { + values.Append(value) + } + } + return values +} + +// Has checks if the provided key exists in the map. +// It returns true if the key exists, and false otherwise. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// exists := newMap.Has("apple") // true +// exists = newMap.Has("orange") // false +func (gomap *Map[K, V]) Has(key K) bool { + _, ok := (*gomap)[key] + return ok +} + +// HasMany checks the existence of multiple keys in the map and returns a slice of boolean values +// indicating whether each corresponding key exists in the map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Check the existence of multiple keys. +// keysToCheck := []string{"apple", "orange", "banana"} +// results := newMap.HasMany(keysToCheck...) +// +// // The resulting boolean slice: {true, false, true} +func (gomap *Map[K, V]) HasMany(keys ...K) *slice.Slice[bool] { + values := make(slice.Slice[bool], len(keys)) + for i, key := range keys { + if gomap.Has(key) { + values.Replace(i, true) + } + } + return &values +} + +// Intersection creates a new map containing key-value pairs that exist in both the current map and another map. +// It compares values using reflect.DeepEqual to determine equality between the pairs. +// It takes another map as input and returns a new map containing the intersecting key-value pairs. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("apple", 5) +// newMap2.Add("orange", 10) +// +// newMap := newMap1.Intersection(newMap2) // Creates a new map with the pair "apple": 5 +func (gomap *Map[K, V]) Intersection(other *Map[K, V]) *Map[K, V] { + return gomap.IntersectionFunc(other, func(key K, a, b V) bool { + return reflect.DeepEqual(a, b) + }) +} + +// IntersectionFunc creates a new map containing key-value pairs that exist in both the current map and another map. +// It takes another map and a condition function as input and adds key-value pairs from the current map to the new gomap +// if the condition function evaluates to true for the corresponding key-value pair in the other map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// newMap1.Add("orange", 8) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 8) +// newMap2.Add("banana", 6) +// +// newMap := newMap1.IntersectionFunc(newMap2, func(key string, a, b int) bool { +// return a == b +// }) // Creates a new map with the pair "orange": 8 +func (gomap *Map[K, V]) IntersectionFunc(other *Map[K, V], fn func(key K, a V, b V) bool) *Map[K, V] { + newMap := make(Map[K, V], 0) + gomap.Each(func(key K, value V) { + if v, ok := other.Get(key); ok && fn(key, value, v) { + newMap.Add(key, value) + } + }) + return &newMap +} + +// IsEmpty checks if the map is empty, i.e., it contains no key-value pairs. +// It returns true if the map is empty and false otherwise. +// +// // Create a new Map instance. +// newMap := make(Map[string, int]) +// empty := newMap.IsEmpty() // Returns true since the map is empty +func (gomap *Map[K, V]) IsEmpty() bool { + return gomap.Length() == 0 +} + +func (gomap *Map[K, V]) IsPopulated() bool { + return !gomap.IsEmpty() +} + +// Keys returns a slice containing all the keys present in the map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Get all keys from the map. +// keys := newMap.Keys() // Result: {"apple", "banana", "cherry"} +func (gomap *Map[K, V]) Keys() *slice.Slice[K] { + keys := make(slice.Slice[K], 0) + gomap.EachKey(func(key K) { + keys.Append(key) + }) + return &keys +} + +// KeysFunc applies the provided function to each key in the map and returns a slice containing the keys for which the function returns true. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Get keys from the map where the key length is greater than 5. +// keys := newMap.KeysFunc(func(key string) bool { +// return len(key) > 5 +// }) +// // Result: {"banana"} +func (gomap *Map[K, V]) KeysFunc(fn func(key K) bool) *slice.Slice[K] { + keys := make(slice.Slice[K], 0) + gomap.EachKey(func(key K) { + if fn(key) { + keys.Append(key) + } + }) + return &keys +} + +// Length returns the number of key-value pairs in the map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// length := newMap.Length() // Result: 3 +func (gomap *Map[K, V]) Length() int { + return len(*gomap) +} + +// Map applies the provided function to each key-value pair in the map and returns a new map containing the mapped key-value pairs. +// The original map remains unchanged. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// +// // Create a new map with doubled values. +// newMap := newMap.Map(func(key string, value int) int { +// return value * 2 +// }) +// // New gomap: {"apple": 10, "banana": 6} +func (gomap *Map[K, V]) Map(fn func(key K, value V) V) *Map[K, V] { + return gomap.MapBreak(func(key K, value V) (V, bool) { + return fn(key, value), true + }) +} + +// MapBreak applies the provided function to each key-value pair in the map. It creates a new map containing the mapped key-value pairs +// until the function returns false for any pair, at which point the mapping breaks early. The original map remains unchanged. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.Add("cherry", 8) +// +// // Create a new map with doubled values until a value is greater than or equal to 10. +// newMap := newMap.MapBreak(func(key string, value int) (int, bool) { +// newValue := value * 2 +// return newValue, newValue < 10 +// }) +// // New gomap: {"apple": 10, "banana": 6} +func (gomap *Map[K, V]) MapBreak(fn func(key K, value V) (V, bool)) *Map[K, V] { + newMap := make(Map[K, V]) + for key, value := range *gomap { + value, ok := fn(key, value) + if !ok { + break + } + newMap.Add(key, value) + } + return &newMap +} + +// Merge merges all key-value pairs from another map into the current map. +// It takes another map as input and adds all its key-value pairs to the current map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 10) +// +// newMap1.Merge(newMap2) // Adds "orange": 10 to newMap1 +func (gomap *Map[K, V]) Merge(other *Map[K, V]) *Map[K, V] { + return gomap.MergeFunc(other, func(key K, value V) bool { return true }) +} + +// MergeFunc merges the key-value pairs from another map into the current map based on a provided condition function. +// It takes another map and a condition function as input and adds key-value pairs from the other map to the current gomap +// if the condition function evaluates to true for the given key-value pair from the other map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// newMap1.Add("orange", 10) +// +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 8) +// newMap2.Add("banana", 6) +// +// // Condition function to merge pairs where the value in newMap2 is greater than 7 +// newMap1.MergeFunc(newMap2, func(key string, value int) bool { +// return value > 7 +// }) // Adds "orange": 8 to newMap1, "banana": 6 does not meet the condition and is not added +func (gomap *Map[K, V]) MergeFunc(other *Map[K, V], fn func(key K, value V) bool) *Map[K, V] { + other.Each(func(key K, value V) { + if fn(key, value) { + gomap.Add(key, value) + } + }) + return gomap +} + +// MergeMany merges key-value pairs from multiple hashtables into the current map. +// It takes a variadic number of hashtables as input and adds all their key-value pairs to the current map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 10) +// // Create a new Map instance. +// newMap3 := make(gomap.Map[string, int]) +// newMap3.Add("banana", 7) +// +// newMap1.MergeMany(newMap2, newMap3) // Merges key-value pairs from newMap2 and newMap3 into newMap1 +func (gomap *Map[K, V]) MergeMany(others ...*Map[K, V]) *Map[K, V] { + for _, other := range others { + gomap.Merge(other) + } + return gomap +} + +// MergeManyFunc merges key-value pairs from multiple hashtables into the current map based on a provided condition function. +// It takes a slice of hashtables and a condition function as input. For each key-value pair in the other hashtables, +// the function is applied, and if it evaluates to true, the pair is added to the current map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 10) +// // Create a new Map instance. +// newMap3 := make(gomap.Map[string, int]) +// newMap3.Add("banana", 7) +// +// // Condition function to include pairs from the second map only if the value is greater than 7 +// newMap1.MergeManyFunc([]*Map[string, int]{newMap2, newMap3}, func(i int, key string, value int) bool { +// return value > 7 +// }) +func (gomap *Map[K, V]) MergeManyFunc(others []*Map[K, V], fn func(i int, key K, value V) bool) *Map[K, V] { + for i, other := range others { + gomap.MergeFunc(other, func(key K, value V) bool { + return fn(i, key, value) + }) + } + return gomap +} + +// Not checks if the given key is not present in the map. +// It returns true if the key is not found, and false if the key exists in the map. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// result := newMap.Not("apple") // Returns true if "apple" is not in the gomap, false otherwise +func (gomap *Map[K, V]) Not(key K) bool { + return !gomap.Has(key) +} + +// NotMany checks if multiple keys are not present in the map. +// It takes a variadic number of keys as input and returns a slice of booleans indicating whether each key is not found in the map. +// For each key, if it is not present in the gomap, the corresponding boolean in the returned slice is true. Otherwise, it is false. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// results := newMap.NotMany("apple", "orange", "banana") +// // Returns a slice containing [true, true, false] indicating "apple" and "orange" are not in the gomap, but "banana" is present +func (gomap *Map[K, V]) NotMany(keys ...K) *slice.Slice[bool] { + values := make(slice.Slice[bool], len(keys)) + for i, key := range keys { + if gomap.Not(key) { + values.Replace(i, true) + } + } + return &values +} + +// Pop removes a key-value pair from the map based on the provided key and returns the removed value. +// If the key is found in the gomap, the corresponding value is returned. If the key is not present, +// the zero value for the value type is returned. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// removedValue := newMap.Pop("apple") // Removes the key "apple" and returns its associated value 5, or 0 if "apple" is not found +func (gomap *Map[K, V]) Pop(key K) V { + value, _ := gomap.PopOK(key) + return value +} + +// PopOK removes a key-value pair from the map 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 gomap, 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. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// removedValue, ok := newMap.PopOK("apple") // Removes the key "apple" and returns its associated value 5, ok is true +// removedValue, ok = newMap.PopOK("banana") // Key "banana" not found, removedValue is 0 and ok is false +func (gomap *Map[K, V]) PopOK(key K) (V, bool) { + value, ok := gomap.Get(key) + if ok { + ok = gomap.DeleteOK(key) + } + return value, ok +} + +// PopMany removes multiple key-value pairs from the map based on the provided keys. +// It takes a variadic number of keys as input and removes the corresponding key-value pairs from the map. +// 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 gomap, the corresponding value in the result slice will be the zero value for the value type. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// removedValues := newMap.PopMany("apple", "orange") // Removes "apple", returns a slice containing [5, 0] +func (gomap *Map[K, V]) PopMany(keys ...K) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + for _, key := range keys { + value, ok := gomap.PopOK(key) + if ok { + values.Append(value) + } + } + return &values +} + +// PopManyFunc removes key-value pairs from the map based on the provided condition function and returns the removed values in a slice. +// It takes a condition function as input and removes key-value pairs for which the function evaluates to true. +// It returns a slice containing the removed values. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("orange", 10) +// +// removedValues := newMap.PopManyFunc(func(key string, value int) bool { +// return value > 7 // Remove values greater than 7 +// }) // Returns a slice containing [10] +func (gomap *Map[K, V]) PopManyFunc(fn func(key K, value V) bool) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + gomap.Each(func(key K, value V) { + if fn(key, value) { + removedValue := gomap.Pop(key) + values.Append(removedValue) + } + }) + return &values +} + +// ReplaceMany iterates over the key-value pairs in the map 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 map with the modified value. +// If the function returns false, the key-value pair is not modified. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("banana", 3) +// newMap.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 +// }) +// // newMap: {"apple": 5, "banana": 6} +func (gomap *Map[K, V]) ReplaceMany(fn func(key K, value V) (V, bool)) *Map[K, V] { + for key, value := range *gomap { + if updatedValue, ok := fn(key, value); ok { + gomap.Add(key, updatedValue) + } + } + return gomap +} + +// TakeFrom transfers all key-value pairs from another map into the current gomap, emptying the other map. +// It takes another map as input and adds all key-value pairs from the other map to the current map. +// +// // Create a new Map instance. +// newMap1 := make(gomap.Map[string, int]) +// newMap1.Add("apple", 5) +// // Create a new Map instance. +// newMap2 := make(gomap.Map[string, int]) +// newMap2.Add("orange", 10) +// +// newMap1.TakeFrom(newMap2) // Transfers "orange": 10 from newMap2 to newMap1, leaving newMap2 empty +func (gomap *Map[K, V]) TakeFrom(other *Map[K, V]) *Map[K, V] { + other.Each(func(key K, value V) { + gomap.Add(key, other.Pop(key)) + }) + return gomap +} + +// Values returns a slice containing all the values present in the map. +// It iterates over the map and collects all the values in the order of insertion. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("orange", 10) +// values := newMap.Values() // Returns a slice containing [5, 10] +func (gomap *Map[K, V]) Values() *slice.Slice[V] { + i := 0 + values := make(slice.Slice[V], gomap.Length()) + gomap.EachValue(func(value V) { + values.Replace(i, value) + i++ + }) + return &values +} + +// ValuesFunc returns a slice containing values from the map 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 map and includes the values in the returned slice for which the condition function evaluates to true. +// +// // Create a new Map instance. +// newMap := make(gomap.Map[string, int]) +// newMap.Add("apple", 5) +// newMap.Add("orange", 10) +// values := newMap.ValuesFunc(func(key string, value int) bool { +// return value > 7 // Include values greater than 7 in the result +// }) // Returns a slice containing [10] +func (gomap *Map[K, V]) ValuesFunc(fn func(key K, value V) bool) *slice.Slice[V] { + values := make(slice.Slice[V], 0) + gomap.Each(func(key K, value V) { + if fn(key, value) { + values.Append(value) + } + }) + return &values +} diff --git a/gomap_test.go b/gomap_test.go new file mode 100644 index 0000000..3395625 --- /dev/null +++ b/gomap_test.go @@ -0,0 +1,1652 @@ +package gomap_test + +import ( + "math" + "reflect" + "sort" + "strings" + "testing" + + "github.com/lindsaygelle/gomap" + "github.com/lindsaygelle/slice" +) + +// TestHasnewMapable tests Map. +func TestHasnewMapable(t *testing.T) { + table := gomap.Map[string, string]{ + "hello": "world"} + t.Log(table) +} + +// TestAdd tests Map.Add. +func TestAdd(t *testing.T) { + // Test case 1: Add key-value pairs to an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + + // Verify the values using Get method. + value, ok := newMap.Get("apple") + if !ok || value != 5 { + t.Errorf("Expected key 'apple' to have value 5, but got %v", value) + } + value, ok = newMap.Get("banana") + if !ok || value != 3 { + t.Errorf("Expected key 'banana' to have value 3, but got %v", value) + } + + // Test case 2: Replace existing key-value pair. + newMap.Add("banana", 10) // Updates the value for the key "banana" to 10. + value, ok = newMap.Get("banana") + if !ok || value != 10 { + t.Errorf("Expected key 'banana' to have updated value 10, but got %v", value) + } + + // Test case 3: Add a new key-value pair. + newMap.Add("cherry", 8) + value, ok = newMap.Get("cherry") + if !ok || value != 8 { + t.Errorf("Expected key 'cherry' to have value 8, but got %v", value) + } + + // Verify that other keys are not affected. + value, ok = newMap.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be absent, but it was found with value %v", value) + } +} + +// TestAddLength tests Map.AddLength. +func TestAddLength(t *testing.T) { + // Test case 1: Add key-value pairs to an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + length := newMap.AddLength("apple", 5) + if length != 1 { + t.Errorf("Expected length of gomap after adding 'apple' with value 5 to be 1, but got %v", length) + } + + // Test case 2: Replace an existing key-value pair. + length = newMap.AddLength("apple", 10) + if length != 1 { + t.Errorf("Expected length of gomap after updating 'apple' with value 10 to be 1, but got %v", length) + } + + // Test case 3: Add a new key-value pair. + length = newMap.AddLength("banana", 3) + if length != 2 { + t.Errorf("Expected length of gomap after adding 'banana' with value 3 to be 2, but got %v", length) + } + + // Test case 4: Add another new key-value pair. + length = newMap.AddLength("cherry", 8) + if length != 3 { + t.Errorf("Expected length of gomap after adding 'cherry' with value 8 to be 3, but got %v", length) + } + + // Verify that other keys are not affected. + value, ok := newMap.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be absent, but it was found with value %v", value) + } +} + +// TestAddMany tests Map.AddMany. + +func TestAddMany(t *testing.T) { + // Test case 1: Add key-value pairs to an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + newMap.AddMany(map[string]int{"orange": 7, "grape": 4}, map[string]int{"kiwi": 6, "pear": 9}) + + // 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 := newMap.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: Replace existing key-value pairs and add new ones. + newMap.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 := newMap.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 3: Add new key-value pairs. + newMap.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 := newMap.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) + } + } + + // Verify that other keys are not affected. + _, ok := newMap.Get("pineapple") + if ok { + t.Errorf("Expected key 'pineapple' to be absent, but it was found") + } +} + +// TestAddManyFunc tests Map.AddManyFunc. +func TestAddManyFunc(t *testing.T) { + // Test case 1: Add key-value pairs with values greater than 0 to the gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + values := []map[string]int{{"apple": 5, "orange": -3, "banana": 10}} + condition := func(i int, key string, value int) bool { + return value > 0 + } + newMap.AddManyFunc(values, condition) + + // Verify that only the key-value pairs with values greater than 0 are added to the gomap. + expected := &gomap.Map[string, int]{"apple": 5, "banana": 10} + if !reflect.DeepEqual(newMap, expected) { + t.Errorf("Expected %v, but got %v", expected, newMap) + } + + // Test case 2: Add all key-value pairs to the gomap. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + 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 + } + newMap.AddManyFunc(values, condition) + + // Verify that all key-value pairs are added to the gomap. + expected = &gomap.Map[string, int]{"apple": 5, "orange": -3, "banana": 10} + if !reflect.DeepEqual(newMap, expected) { + t.Errorf("Expected %v, but got %v", expected, newMap) + } +} + +// TestAddManyOK tests Map.AddManyOK. +func TestAddManyOK(t *testing.T) { + // Test case 1: Add key-value pairs to an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + results := newMap.AddManyOK( + map[string]int{"apple": 5}, + map[string]int{"banana": 3}, + map[string]int{"banana": 10}, + map[string]int{"cherry": 8}, + ) + + // 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 the added and updated key-value pairs. + expected := map[string]int{"apple": 5, "cherry": 8} + for key, expectedValue := range expected { + value, ok := newMap.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: Add new key-value pairs. + results = newMap.AddManyOK( + map[string]int{"watermelon": 12, "pineapple": 7}, + ) + + // 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 the added key-value pairs. + expected = map[string]int{"apple": 5, "cherry": 8, "watermelon": 12, "pineapple": 7} + for key, expectedValue := range expected { + value, ok := newMap.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) + } + } +} + +// TestAddOK tests Map.AddOK. +func TestAddOK(t *testing.T) { + newMap := make(gomap.Map[string, int]) + + // Test adding a new key-value pair. + added := newMap.AddOK("apple", 5) + if !added { + t.Error("Expected 'apple' to be added, but it was not.") + } + + // Check if the key-value pair is added correctly. + value, exists := newMap["apple"] + if !exists || value != 5 { + t.Fatalf("Expected key 'apple' with value '5', but got value '%d'", value) + } + + // Test adding an existing key. + reAdded := newMap.AddOK("apple", 10) + if reAdded { + t.Error("Expected 'apple' to not be re-added, but it was.") + } + + // Check if the value for 'apple' remains unchanged. + value, exists = newMap["apple"] + if !exists || value != 5 { + t.Fatalf("Expected key 'apple' to have value '5' after re-adding attempt, but got value '%d'", value) + } + + // Test adding another new key-value pair. + addedNew := newMap.AddOK("banana", 3) + if !addedNew { + t.Error("Expected 'banana' to be added, but it was not.") + } + + // Check if the key-value pair for 'banana' is added correctly. + value, exists = newMap["banana"] + if !exists || value != 3 { + t.Fatalf("Expected key 'banana' with value '3', but got value '%d'", value) + } +} + +// TestContains tests Map.Contains. +func TestContains(t *testing.T) { + // Test case 1: Check for a value in an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + key, found := newMap.Contains(5) // Check if value 5 is in the gomap. + + // Since the gomap is empty, the result should be ("", false). + if key != "" || found { + t.Errorf("Expected ('', false), but got (%s, %t)", key, found) + } + + // Test case 2: Check for a value in a non-empty gomap. + newMap = &gomap.Map[string, int]{} // Create a new gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 10) + newMap.Add("orange", 5) + + // Check if value 5 is in the gomap. + key, found = newMap.Contains(5) + + // Since value 5 exists in the gomap, the result should be ("apple", true). + if !found { + t.Errorf("Expected ('%s', true), but got (%s, %t)", key, key, found) + } + + // Test case 3: Check for a value that doesn't exist in the gomap. + key, found = newMap.Contains(15) // Check if value 15 is in the gomap. + + // Since value 15 doesn't exist in the gomap, the result should be ("", false). + if key != "" || found { + t.Errorf("Expected ('', false), but got (%s, %t)", key, found) + } +} + +// TestDelete tests Map.Delete. +func TestDelete(t *testing.T) { + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + + // Test case 1: Delete an existing key. + newMap.Delete("apple") + if _, ok := newMap["apple"]; ok { + t.Fatalf("Expected key 'apple' to be deleted, but it still exists in the gomap") + } + + // Test case 2: Delete a non-existing key. + newMap.Delete("nonexistent") + if _, ok := newMap["nonexistent"]; ok { + t.Fatalf("Expected key 'nonexistent' to not exist, but it was found in the gomap") + } + + // Test case 3: Delete a key after adding it again. + newMap["apple"] = 10 + newMap.Delete("apple") + if _, ok := newMap["apple"]; ok { + t.Fatalf("Expected key 'apple' to be deleted, but it still exists in the gomap") + } +} + +// TestDeleteLength tests Map.DeleteLength. +func TestDeleteLength(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap and get the initial length. + newMap["apple"] = 5 + newMap["banana"] = 3 + initialLength := len(newMap) + + // Delete an existing key from the gomap and get the updated length. + lengthAfterDelete := newMap.DeleteLength("apple") + + // Expected length after deleting "apple": initial length - 1. + expectedLength := initialLength - 1 + + // Verify that the obtained length matches the expected length. + if lengthAfterDelete != expectedLength { + t.Fatalf("Expected length: %d, but got: %d", expectedLength, lengthAfterDelete) + } + + // Attempt to delete a non-existing key from the gomap. + lengthAfterNonExistingDelete := newMap.DeleteLength("grape") + + // Length should remain the same after attempting to delete a non-existing key. + // Expected length: initial length. + expectedLengthNonExisting := len(newMap) + + // Verify that the obtained length matches the expected length. + if lengthAfterNonExistingDelete != expectedLengthNonExisting { + t.Fatalf("Expected length: %d, but got: %d", expectedLengthNonExisting, lengthAfterNonExistingDelete) + } +} + +// TestDeleteMany tests Map.DeleteMany. +func TestDeleteMany(t *testing.T) { + // Test case 1: Delete keys from an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + newMap.DeleteMany("apple", "banana") // Attempt to delete keys "apple" and "banana". + + // The gomap should remain empty. + if !newMap.IsEmpty() { + t.Errorf("Expected gomap to be empty, but got non-empty gomap: %v", newMap) + } + + // Test case 2: Delete keys from a non-empty gomap. + newMap = &gomap.Map[string, int]{} // Create a new gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 10) + newMap.Add("orange", 3) + + // Delete keys "apple" and "banana". + newMap.DeleteMany("apple", "banana") + + // Verify that "apple" and "banana" are deleted, and "orange" remains in the gomap. + expected := &slice.Slice[string]{"orange"} + result := newMap.Keys() + + if !reflect.DeepEqual(expected, result) { + t.Errorf("Expected keys %v after deleting 'apple' and 'banana', but got keys %v", expected, result) + } +} + +// TestDeleteManyOK tests Map.DeleteManyOK. +func TestDeleteManyOK(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + + // Specify keys to delete. + keysToDelete := []string{"apple", "grape"} + + // Attempt to delete keys and check if deletion is successful. + results := newMap.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.Fatalf("Expected deletion of key %s to be %v but got %v", keysToDelete[i], expectedResults[i], result) + } + } +} + +// TestDeleteManyValues tests Map.DeleteManyValues. +func TestDeleteManyValues(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap.Add("apple", 5) + newMap.Add("banana", 3) + newMap.Add("cherry", 8) + + // Delete key-value pairs where the value is 3 or 8. + newMap.DeleteManyValues(3, 8) + + // Verify that the gomap only contains the expected key-value pair. + expected := gomap.Map[string, int]{"apple": 5} + if !reflect.DeepEqual(newMap, expected) { + t.Fatalf("Expected gomap: %v, but got: %v", expected, newMap) + } +} + +// TestDeleteOK tests Map.DeleteOK. +func TestDeleteOK(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + + // Delete keys and check if deletion is successful. + deleted := newMap.DeleteOK("apple") + if !deleted { + t.Fatalf("Expected deletion of 'apple' to be successful") + } + + // Attempt to delete a key that does not exist. + notDeleted := newMap.DeleteOK("grape") + if !notDeleted { + 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 := newMap.DeleteOK("apple") + if !alreadyDeleted { + t.Fatalf("Expected deletion of 'apple' to be successful even though it was already deleted") + } +} + +// TestEach tests Map.Each. +func TestEach(t *testing.T) { + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Define a map to store the expected output. + expectedOutput := map[string]int{ + "apple": 5, + "banana": 3, + "cherry": 8, + } + + // Define a function to compare the actual output with the expected output. + printKeyValue := func(key string, value int) { + if expected, ok := expectedOutput[key]; ok { + if value != expected { + t.Fatalf("Expected %s: %d, but got %d", key, expected, value) + } + delete(expectedOutput, key) + } else { + t.Fatalf("Unexpected key: %s", key) + } + } + + // Call the Each function with the printKeyValue function. + newMap.Each(printKeyValue) + + // Check if all expected keys have been processed. + if len(expectedOutput) > 0 { + t.Fatalf("Not all keys were processed: %v", expectedOutput) + } +} + +// TestEachBreak tests Map.EachBreak. +func TestEachBreak(t *testing.T) { + var stopPrinting string + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + newMap.Add("cherry", 8) + + // Define a function to stop iteration when key is "banana". + newMap.EachBreak(func(key string, value int) bool { + t.Logf("%s %d", key, value) + stopPrinting = key + return key != "banana" + }) + + // Ensure that iteration stopped at "banana". + if stopPrinting != "banana" { + t.Fatalf("Iteration did not stop at 'banana'.") + } +} + +// TestEachKey tests Map.EachKey. +func TestEachKey(t *testing.T) { + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Define a function to print each key. + var printedKeys []string + printKey := func(key string) { + printedKeys = append(printedKeys, key) + } + + // Iterate over the keys and print each key. + newMap.EachKey(printKey) + + // Sort the printed values for consistent comparison. + sort.Strings(printedKeys) + + // Expected output: "apple", "banana", "cherry". + expectedKeys := []string{"apple", "banana", "cherry"} + for i, key := range printedKeys { + if key != expectedKeys[i] { + t.Fatalf("Expected key %s at index %d, but got %s", expectedKeys[i], i, key) + } + } +} + +// TestEachKeyBreak tests Map.EachKeyBreak. +func TestEachKeyBreak(t *testing.T) { + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + var keyToBreak string + newMap.EachBreak(func(key string, value int) bool { + keyToBreak = key + return key != "banana" + }) + + if keyToBreak != "banana" { + t.Fatalf("Expect keyToBreak to equal 'banana', but got %s", keyToBreak) + } +} + +// TestEachValue tests Map.EachValue. +func TestEachValue(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Define a function to print each value. + var printedValues []int + printValue := func(value int) { + printedValues = append(printedValues, value) + } + + // Iterate over the gomap values and print them. + newMap.EachValue(printValue) + + // Sort the printed values for consistent comparison. + sort.Ints(printedValues) + + // Expected output: 3, 5, 8. + expectedValues := []int{3, 5, 8} + + if len(printedValues) != len(expectedValues) { + t.Fatalf("Expected %d values, but got %d", len(expectedValues), len(printedValues)) + return + } + + for i, value := range printedValues { + if value != expectedValues[i] { + t.Fatalf("Expected value %d at index %d, but got %d", expectedValues[i], i, value) + } + } +} + +// TestEachValueBreak tests Map.EachValueBreak. + +func TestEachValueBreak(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + newMap.Add("cherry", 8) + + keys := make([]string, 0, len(newMap)) + for key := range newMap { + 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. + var processedValues []int + processValue := func(value int) bool { + processedValues = append(processedValues, value) + return value != 3 + } + + // Iterate over the gomap values and process them until the value is 3. + for _, key := range keys { + value, _ := newMap.Get(key) + if !processValue(value) { + break + } + } + + // Expected output: 5, 3. + expectedValues := []int{5, 3} + for i, value := range processedValues { + if value != expectedValues[i] { + t.Fatalf("Expected value %d at index %d, but got %d", expectedValues[i], i, value) + } + } +} + +// TestEmptyInto tests Map.EmptyInto. +func TestEmptyInto(t *testing.T) { + // Test case 1: Transfer from an empty gomap to another empty gomap. + newMap1 := &gomap.Map[string, int]{} // Create an empty source gomap. + newMap2 := &gomap.Map[string, int]{} // Create an empty destination gomap. + newMap1.EmptyInto(newMap2) // Transfer from newMap1 to newMap2. + + // Verify that newMap1 is empty. + if newMap1.Length() != 0 { + t.Errorf("Expected source gomap to be empty after transfer, but it has %d items", newMap1.Length()) + } + + // Verify that newMap2 is still empty. + if newMap2.Length() != 0 { + t.Errorf("Expected destination gomap to be empty after transfer, but it has %d items", newMap2.Length()) + } + + // Test case 2: Transfer from a non-empty gomap to an empty gomap. + newMap1 = &gomap.Map[string, int]{} // Create an empty source gomap. + newMap1.Add("apple", 5) + newMap2 = &gomap.Map[string, int]{} // Create an empty destination gomap. + newMap1.EmptyInto(newMap2) // Transfer from newMap1 to newMap2. + + // Verify that newMap1 is empty. + if newMap1.Length() != 0 { + t.Errorf("Expected source gomap to be empty after transfer, but it has %d items", newMap1.Length()) + } + + // Verify that newMap2 contains the transferred key-value pair. + expectedValue := 5 + transferredValue, ok := newMap2.Get("apple") + if !ok || transferredValue != expectedValue { + t.Errorf("Expected destination gomap to contain 'apple': %d after transfer, but it contains '%d'", expectedValue, transferredValue) + } +} + +// TestEqual tests Map.Equal. +func TestEqual(t *testing.T) { + // Test case 1: Compare equal hasnewMapables. + newMap1 := &gomap.Map[string, int]{} // Create a new gomap. + newMap1.Add("apple", 5) + newMap1.Add("orange", 10) + + newMap2 := &gomap.Map[string, int]{} // Create another gomap with similar values. + newMap2.Add("apple", 5) + newMap2.Add("orange", 10) + + // Check if the two hasnewMapables are equal. + equal := newMap1.Equal(newMap2) + + // Since newMap1 and newMap2 have the same key-value pairs, they are considered equal. + if !equal { + t.Errorf("Expected true, but got false") + } + + // Test case 2: Compare unequal hasnewMapables. + newMap3 := &gomap.Map[string, int]{} // Create a new gomap. + newMap3.Add("apple", 5) + newMap3.Add("orange", 10) + + newMap4 := &gomap.Map[string, int]{} // Create another gomap with different values for "orange". + newMap4.Add("apple", 5) + newMap4.Add("orange", 12) + + // Check if the two hasnewMapables are equal. + equal = newMap3.Equal(newMap4) + + // Since newMap3 and newMap4 have different values for "orange", they are not considered equal. + if equal { + t.Errorf("Expected false, but got true") + } +} + +// TestEqualFunc tests Map.EqualFunc. +func TestEqualFunc(t *testing.T) { + // Custom comparison function to check if two integers are equal when their difference is less than or equal to 1 + compareFunc := func(a, b int) bool { + return math.Abs(float64(a-b)) <= 1 + } + + // Test case 1: Compare equal hasnewMapables based on the custom comparison function. + newMap1 := &gomap.Map[string, int]{} // Create a new gomap. + newMap1.Add("apple", 5) + newMap1.Add("orange", 10) + + newMap2 := &gomap.Map[string, int]{} // Create another gomap with similar values. + newMap2.Add("apple", 5) + newMap2.Add("orange", 11) // The difference between 10 and 11 is within the allowed range according to compareFunc. + + // Check if the two hasnewMapables are equal based on the custom comparison function. + equal := newMap1.EqualFunc(newMap2, compareFunc) + + // Since the values for "orange" (10 and 11) have a difference of 1, within the allowed range, + // the hasnewMapables are considered equal according to the custom comparison function. + if !equal { + t.Errorf("Expected true, but got false") + } + + // Test case 2: Compare unequal hasnewMapables based on the custom comparison function. + newMap3 := &gomap.Map[string, int]{} // Create a new gomap. + newMap3.Add("apple", 5) + newMap3.Add("orange", 10) + + newMap4 := &gomap.Map[string, int]{} // Create another gomap with different values for "orange". + newMap4.Add("apple", 5) + newMap4.Add("orange", 12) // The difference between 10 and 12 is greater than the allowed range according to compareFunc. + + // Check if the two hasnewMapables are equal based on the custom comparison function. + equal = newMap3.EqualFunc(newMap4, compareFunc) + + // Since the difference between 10 and 12 is greater than the allowed range according to compareFunc, + // the hasnewMapables are not considered equal based on the custom comparison function. + if equal { + t.Errorf("Expected false, but got true") + } +} + +// TestEqualLength tests Map.EqualLength. +func TestEqualLength(t *testing.T) { + // Test case 1: Compare hasnewMapables with equal length. + newMap1 := &gomap.Map[string, int]{} // Create a new gomap. + newMap1.Add("apple", 5) + newMap1.Add("orange", 10) + + newMap2 := &gomap.Map[string, int]{} // Create another gomap with the same number of key-value pairs. + newMap2.Add("apple", 5) + newMap2.Add("orange", 7) + + // Check if the two hasnewMapables have equal length. + equalLength := newMap1.EqualLength(newMap2) + + // Since newMap1 and newMap2 have the same number of key-value pairs, they are considered equal in length. + if !equalLength { + t.Errorf("Expected true, but got false") + } + + // Test case 2: Compare hasnewMapables with unequal length. + newMap3 := &gomap.Map[string, int]{} // Create a new gomap. + newMap3.Add("apple", 5) + + newMap4 := &gomap.Map[string, int]{} // Create another gomap with a different number of key-value pairs. + newMap4.Add("apple", 5) + newMap4.Add("orange", 7) + + // Check if the two hasnewMapables have equal length. + equalLength = newMap3.EqualLength(newMap4) + + // Since newMap3 and newMap4 have different numbers of key-value pairs, they are not considered equal in length. + if equalLength { + t.Errorf("Expected false, but got true") + } +} + +// TestFetch tests Map.Fetch. +func TestFetch(t *testing.T) { + // Test case 1: Fetch value for an existing key. + newMap := &gomap.Map[string, int]{} // Create a new gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + + // Fetch the value associated with the key "apple". + fetchedValue := newMap.Fetch("apple") + + // Since "apple" is in the gomap, the fetched value should be 5. + expectedValue := 5 + if fetchedValue != expectedValue { + t.Errorf("Expected %d, but got %d", expectedValue, fetchedValue) + } + + // Test case 2: Fetch value for a non-existing key. + // Fetch the value associated with the key "banana", which is not in the gomap. + fetchedValue = newMap.Fetch("banana") + + // Since "banana" is not in the gomap, the fetched value should be the zero value for int, which is 0. + expectedValue = 0 + if fetchedValue != expectedValue { + t.Errorf("Expected %d, but got %d", expectedValue, fetchedValue) + } +} + +// TestFilter tests Map.Filter. +func TestFilter(t *testing.T) { + // Test case 1: Filter with an empty gomap and a function that never selects any pairs. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + filterFunc := func(key string, value int) bool { + return false // Never select any values + } + filtered := newMap.Filter(filterFunc) + expected := &gomap.Map[string, int]{} // Expected empty gomap. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } + + // Test case 2: Filter with a non-empty gomap and a function that never selects any pairs. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + filterFunc = func(key string, value int) bool { + return false // Never select any values + } + filtered = newMap.Filter(filterFunc) + expected = &gomap.Map[string, int]{} // Expected empty gomap. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } + + // Test case 3: Filter with a non-empty gomap and a function that selects certain pairs. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + newMap.Add("banana", 3) + filterFunc = func(key string, value int) bool { + return value > 4 // Select pairs where value is greater than 4 + } + filtered = newMap.Filter(filterFunc) + expected = &gomap.Map[string, int]{"apple": 5, "orange": 10} // Expected filtered gomap. + if !reflect.DeepEqual(filtered, expected) { + t.Errorf("Expected %v, but got %v", expected, filtered) + } +} + +// TestGet tests Map.Get. + +func TestGet(t *testing.T) { + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + + // Test case 1: Get an existing key. + value, exists := newMap.Get("apple") + if !exists { + t.Fatalf("Expected key 'apple' to exist, but it was not found in the gomap") + } + if value != 5 { + t.Fatalf("Expected value for key 'apple' to be 5, but got %d", value) + } + + // Test case 2: Get a non-existing key. + value, exists = newMap.Get("orange") + if exists { + t.Fatalf("Expected key 'orange' to not exist, but it was found in the gomap with value %d", value) + } + if value != 0 { + t.Fatalf("Expected default value for non-existing key 'orange' to be 0, but got %d", value) + } +} + +// TestGetMany tests Map.GetMany. +func TestGetMany(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Get values for specific keys. + values := newMap.GetMany("apple", "banana", "orange") + + // Sort the keys for consistent iteration order. + sort.Ints(*values) + + // The expected values slice: {5, 3}. + expectedValues := &slice.Slice[int]{5, 3} + + // Sort the keys for consistent iteration order. + sort.Ints(*expectedValues) + + // Verify that the obtained values match the expected values. + if values == expectedValues { + t.Fatalf("Expected values: %v, but got: %v", expectedValues, values) + } +} + +// TestHas tests Map.Has. + +func TestHas(t *testing.T) { + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + + // Test case 1: Key exists in the gomap. + if !newMap.Has("apple") { + t.Fatalf("Expected key 'apple' to exist, but it was not found in the gomap") + } + + // Test case 2: Key does not exist in the gomap. + if newMap.Has("orange") { + t.Fatalf("Expected key 'orange' to not exist, but it was found in the gomap") + } +} + +// TestHasMany test Map.HasMany. +func TestHasMany(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Keys to check existence. + keysToCheck := []string{"apple", "orange", "banana"} + + // Check the existence of multiple keys. + results := newMap.HasMany(keysToCheck...) + + // The expected boolean slice: {true, false, true}. + expectedResults := &slice.Slice[bool]{true, false, true} + + // Verify that the obtained results match the expected results. + if !reflect.DeepEqual(results, expectedResults) { + t.Fatalf("Expected results: %v, but got: %v", expectedResults, results) + } +} + +// TestIntersectionFunc tests Map.IntersectionFunc. +func TestIntersectionFunc(t *testing.T) { + // Test case: Check intersection of two hasnewMapables with common key-value pairs. + newMap1 := &gomap.Map[string, int]{ + "apple": 5, + "orange": 8, + } + newMap2 := &gomap.Map[string, int]{ + "orange": 8, + "banana": 6, + } + + // Condition function to check if values are equal. + conditionFunc := func(key string, a, b int) bool { + return a == b + } + + newHasnewMapable := newMap1.IntersectionFunc(newMap2, conditionFunc) + + expectedHasnewMapable := &gomap.Map[string, int]{ + "orange": 8, + } + + if !reflect.DeepEqual(expectedHasnewMapable, newHasnewMapable) { + t.Errorf("Expected intersection result to be %v, but got %v", expectedHasnewMapable, newHasnewMapable) + } + + // Test case: Check intersection of two hasnewMapables with no common key-value pairs. + newMap1 = &gomap.Map[string, int]{ + "apple": 5, + "orange": 8, + } + newMap2 = &gomap.Map[string, int]{ + "banana": 10, + "grape": 7, + } + + newHasnewMapable = newMap1.IntersectionFunc(newMap2, conditionFunc) + + expectedHasnewMapable = &gomap.Map[string, int]{} + + if !reflect.DeepEqual(expectedHasnewMapable, newHasnewMapable) { + t.Errorf("Expected intersection result to be %v, but got %v", expectedHasnewMapable, newHasnewMapable) + } + + // Test case: Check intersection of empty hasnewMapables. + newMap1 = &gomap.Map[string, int]{} + newMap2 = &gomap.Map[string, int]{} + + newHasnewMapable = newMap1.IntersectionFunc(newMap2, conditionFunc) + + expectedHasnewMapable = &gomap.Map[string, int]{} + + if !reflect.DeepEqual(expectedHasnewMapable, newMap1) { + t.Errorf("Expected intersection result to be %v, but got %v", expectedHasnewMapable, newHasnewMapable) + } +} + +// TestKeys tests Map.Keys. +func TestKeys(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Get all keys from the gomap. + keys := newMap.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.Fatalf("Expected keys: %v, but got: %v", expectedKeys, keys) + } +} + +// TestKeysFunc tests Map.Keys. +func TestKeysFunc(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Get keys from the gomap where the key length is greater than 5. + keys := newMap.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.Fatalf("Expected keys: %v, but got: %v", expectedKeys, keys) + } +} + +// TestLength tests Map.Length. +func TestLength(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["cherry"] = 8 + + // Get the length of the gomap. + length := newMap.Length() + + // Expected length: 3. + expectedLength := 3 + + // Verify that the obtained length matches the expected length. + if length != expectedLength { + t.Fatalf("Expected length: %d, but got: %d", expectedLength, length) + } +} + +func TestMap(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["apple"] = 5 + newMap["banana"] = 3 + newMap["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 gomap. + doubledHT := newMap.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 gomap remains unchanged. + for key, expectedValue := range expectedValues { + value, exists := newMap[key] + if !exists || value != expectedValue/2 { + t.Fatalf("Expected original value %d for key %s, but got %d", expectedValue/2, key, value) + } + } +} + +// TestMapBreak tests Map.MapBreak. +func TestMapBreak(t *testing.T) { + // Create a new gomap. + newMap := make(gomap.Map[string, int]) + + // Add key-value pairs to the gomap. + newMap["banana"] = 3 + + // Apply the MapBreak function to modify values and break the iteration at "banana". + newMap.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 := newMap.Get(key) + if !exists || value != expectedValue { + t.Fatalf("Expected value %d for key %s, but got %d", expectedValue, key, value) + } + } +} + +// TestMerge tests Map.Merge. +func TestMerge(t *testing.T) { + // Test case: Merge all key-value pairs from another gomap. + newMap1 := &gomap.Map[string, int]{} // Create a new gomap. + newMap1.Add("apple", 5) + + newMap2 := &gomap.Map[string, int]{} // Create another gomap. + newMap2.Add("orange", 10) + + // Merge all key-value pairs from newMap2 into newMap1. + newMap1.Merge(newMap2) + + // After merging, newMap1 should contain: {"apple": 5, "orange": 10} + expectedHasnewMapable := &gomap.Map[string, int]{ + "apple": 5, + "orange": 10, + } + + // Verify that newMap1 is equal to the expected gomap. + if !reflect.DeepEqual(expectedHasnewMapable, newMap1) { + t.Errorf("Merge did not produce the expected result. Got: %v, Expected: %v", newMap1, expectedHasnewMapable) + } +} + +// TestMergeFunc tests Map.MergeFunc. +func TestMergeFunc(t *testing.T) { + // Test case: Merge key-value pairs based on the condition function. + newMap1 := &gomap.Map[string, int]{} // Create a new gomap. + newMap1.Add("apple", 5) + newMap1.Add("orange", 10) + + newMap2 := &gomap.Map[string, int]{} // Create another gomap. + newMap2.Add("orange", 8) + newMap2.Add("banana", 6) + + // Condition function to merge pairs where the value in newMap2 is greater than 7. + conditionFunc := func(key string, value int) bool { + return value > 7 + } + + // Merge key-value pairs from newMap2 into newMap1 based on the condition function. + newMap1.MergeFunc(newMap2, conditionFunc) + + // After merging, newMap1 should contain: {"apple": 5, "orange": 8} + expectedHasnewMapable := &gomap.Map[string, int]{ + "apple": 5, + "orange": 8, + } + + // Verify that newMap1 is equal to the expected gomap. + if !newMap1.Equal(expectedHasnewMapable) { + t.Errorf("MergeFunc did not produce the expected result. Got: %v, Expected: %v", newMap1, expectedHasnewMapable) + } +} + +// TestMergeMany tests Map.MergeMany. +func TestMergeMany(t *testing.T) { + // Create hasnewMapables for merging. + newMap1 := &gomap.Map[string, int]{ + "apple": 5, + "orange": 10, + } + newMap2 := &gomap.Map[string, int]{ + "orange": 15, + "banana": 7, + } + newMap3 := &gomap.Map[string, int]{ + "grape": 8, + "melon": 12, + } + + // Merge key-value pairs from newMap2 and newMap3 into newMap1. + newMap1.MergeMany(newMap2, newMap3) + + // Expected merged gomap. + expectedHasnewMapable := &gomap.Map[string, int]{ + "apple": 5, + "orange": 15, + "banana": 7, + "grape": 8, + "melon": 12, + } + + // Check if the merged gomap matches the expected gomap. + if !reflect.DeepEqual(expectedHasnewMapable, newMap1) { + t.Errorf("Merged gomap does not match the expected gomap. Got: %v, Expected: %v", newMap1, expectedHasnewMapable) + } + + // Test case for merging an empty gomap. + emptyHasnewMapable := &gomap.Map[string, int]{} + newMap1.MergeMany(emptyHasnewMapable) + + // Merged gomap should remain unchanged. + if !reflect.DeepEqual(expectedHasnewMapable, newMap1) { + t.Errorf("Merged gomap should remain unchanged after merging with an empty gomap. Got: %v, Expected: %v", newMap1, expectedHasnewMapable) + } +} + +// TestMergeManyFunc tests Map.MergeManyFunc. +func TestMergeManyFunc(t *testing.T) { + // Test case: Merge key-value pairs based on a condition function. + newMap1 := &gomap.Map[string, int]{} // Create an empty destination gomap. + newMap2 := &gomap.Map[string, int]{} // Create the first source gomap. + newMap2.Add("apple", 5) + newMap3 := &gomap.Map[string, int]{} // Create the second source gomap. + newMap3.Add("orange", 10) + newMap4 := &gomap.Map[string, int]{} // Create the third source gomap. + newMap4.Add("banana", 7) + + // Condition function to include pairs only if the value is greater than 7. + conditionFunc := func(i int, key string, value int) bool { + return value >= 7 + } + + // Merge key-value pairs based on the condition function. + mergedHasnewMapable := newMap1.MergeManyFunc([]*gomap.Map[string, int]{newMap2, newMap3, newMap4}, conditionFunc) + + // Verify that the merged gomap contains the expected key-value pairs. + expectedPairs := map[string]int{ + "orange": 10, + "banana": 7, + } + for key, expectedValue := range expectedPairs { + value, ok := mergedHasnewMapable.Get(key) + if !ok || value != expectedValue { + t.Errorf("Expected merged gomap to contain key '%s': %d, but it contains '%d'", key, expectedValue, value) + } + } + + // Verify that unwanted pairs are not present in the merged gomap. + unwantedPairs := map[string]int{ + "apple": 5, + } + for key := range unwantedPairs { + _, ok := mergedHasnewMapable.Get(key) + if ok { + t.Errorf("Expected merged gomap not to contain key '%s', but it was found", key) + } + } +} + +// TestNot tests Map.Not. +func TestNot(t *testing.T) { + // Test case 1: Check if a key is not present in an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + result := newMap.Not("apple") // Check if "apple" is not in the gomap. + expected := true // "apple" is not present in the empty gomap. + + 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 gomap. + newMap.Add("orange", 5) + newMap.Add("banana", 10) + result = newMap.Not("banana") // Check if "banana" is not in the gomap. + expected = false // "banana" is present in the gomap. + + 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 gomap. + newMap.Delete("banana") // Delete "banana" from the gomap. + result = newMap.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 Map.Pop. +func TestPop(t *testing.T) { + // Test case 1: Pop from an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + removedValue := newMap.Pop("apple") + expectedValue := 0 // No key "apple" in the empty gomap. + + if removedValue != expectedValue { + t.Errorf("Expected removed value to be %d, but got %d", expectedValue, removedValue) + } + + // Test case 2: Pop from a non-empty gomap where the key is present. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + removedValue = newMap.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 := newMap.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + + // Test case 3: Pop from a non-empty gomap where the key is not present. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + removedValue = newMap.Pop("banana") + expectedValue = 0 // No key "banana" in the gomap. + + if removedValue != expectedValue { + t.Errorf("Expected removed value to be %d, but got %d", expectedValue, removedValue) + } +} + +// TestPopOK tests Map.PopOK. +func TestPopOK(t *testing.T) { + // Test case 1: Pop from an empty gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + removedValue, ok := newMap.PopOK("apple") + if ok || removedValue != 0 { + t.Errorf("Expected (0, false), but got (%d, %v)", removedValue, ok) + } + + // Test case 2: Pop from a non-empty gomap where the key is present. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + removedValue, ok = newMap.PopOK("apple") + if !ok || removedValue != 5 { + t.Errorf("Expected (5, true), but got (%d, %v)", removedValue, ok) + } + // Verify that the key is removed. + _, ok = newMap.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + + // Test case 3: Pop from a non-empty gomap where the key is not present. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + removedValue, ok = newMap.PopOK("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 gomap. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + removedValues := newMap.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 gomap where some keys are present. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("banana", 3) + newMap.Add("cherry", 8) + removedValues = newMap.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 := newMap.Get("apple") + if ok { + t.Errorf("Expected key 'apple' to be removed, but it was found") + } + _, ok = newMap.Get("orange") + if ok { + t.Errorf("Expected key 'orange' to be removed, but it was found") + } + _, ok = newMap.Get("cherry") + if ok { + t.Errorf("Expected key 'cherry' to be removed, but it was found") + } + _, ok = newMap.Get("grape") + if ok { + t.Errorf("Expected key 'grape' to be removed, but it was found") + } +} + +// TestPopManyFunc tests Map.PopManyFunc. +func TestPopManyFunc(t *testing.T) { + // Test case 1: Pop values greater than 7 from the gomap. + newMap := &gomap.Map[string, int]{ + "apple": 5, + "orange": 10, + "banana": 8, + "grape": 12, + } + + removeCondition := func(key string, value int) bool { + return value > 7 + } + + removedValues := newMap.PopManyFunc(removeCondition) + + sort.Ints(*removedValues) + + expectedRemovedValues := &slice.Slice[int]{8, 10, 12} + + if !reflect.DeepEqual(expectedRemovedValues, removedValues) { + t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) + } + + // Test case 2: Pop values when condition does not match any key-value pairs. + newMap = &gomap.Map[string, int]{ + "apple": 5, + "orange": 3, + "banana": 8, + } + + removeCondition = func(key string, value int) bool { + return value > 10 + } + + removedValues = newMap.PopManyFunc(removeCondition) + + expectedRemovedValues = &slice.Slice[int]{} // No values match the condition. + + if !reflect.DeepEqual(expectedRemovedValues, removedValues) { + t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) + } + + // Test case 3: Pop values from an empty gomap. + newMap = &gomap.Map[string, int]{} + + removeCondition = func(key string, value int) bool { + return value > 0 + } + + removedValues = newMap.PopManyFunc(removeCondition) + + expectedRemovedValues = &slice.Slice[int]{} // No values to remove from an empty gomap. + + if !reflect.DeepEqual(expectedRemovedValues, removedValues) { + t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) + } +} + +// TestReplaceMany tests Map.ReplaceMany. +func TestUpdate(t *testing.T) { + // Test case 1: Replace with an empty gomap and a function that never modifies any pairs. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + replaceFunc := func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + newMap.ReplaceMany(replaceFunc) + expected := &gomap.Map[string, int]{} // Expected empty gomap. + if !reflect.DeepEqual(newMap, expected) { + t.Errorf("Expected %v, but got %v", expected, newMap) + } + + // Test case 2: Replace with a non-empty gomap and a function that never modifies any pairs. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + replaceFunc = func(key string, value int) (int, bool) { + return value, false // Never modify any values + } + newMap.ReplaceMany(replaceFunc) + expected = &gomap.Map[string, int]{"apple": 5, "orange": 10} // Expected same gomap. + if !reflect.DeepEqual(newMap, expected) { + t.Errorf("Expected %v, but got %v", expected, newMap) + } + + // Test case 3: Replace with a non-empty gomap and a function that modifies certain pairs. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.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 + } + newMap.ReplaceMany(replaceFunc) + expected = &gomap.Map[string, int]{"apple": 10, "orange": 10} // Expected modified gomap. + if !reflect.DeepEqual(newMap, expected) { + t.Errorf("Expected %v, but got %v", expected, newMap) + } +} + +// TestTakeFrom tests Map.TakeFrom. +func TestTakeFrom(t *testing.T) { + // Test case 1: Transfer from an empty gomap to another empty gomap. + newMap1 := &gomap.Map[string, int]{} // Create an empty destination gomap. + newMap2 := &gomap.Map[string, int]{} // Create an empty source gomap. + newMap1.TakeFrom(newMap2) // Transfer from newMap2 to newMap1. + + // Verify that newMap1 is still empty. + if newMap1.Length() != 0 { + t.Errorf("Expected destination gomap to be empty after transfer, but it has %d items", newMap1.Length()) + } + + // Verify that newMap2 is still empty. + if newMap2.Length() != 0 { + t.Errorf("Expected source gomap to be empty after transfer, but it has %d items", newMap2.Length()) + } + + // Test case 2: Transfer from a non-empty gomap to an empty gomap. + newMap1 = &gomap.Map[string, int]{} // Create an empty destination gomap. + newMap2 = &gomap.Map[string, int]{} // Create a source gomap. + newMap2.Add("orange", 10) + newMap1.TakeFrom(newMap2) // Transfer from newMap2 to newMap1. + + // Verify that newMap1 contains the transferred key-value pair. + expectedValue := 10 + transferredValue, ok := newMap1.Get("orange") + if !ok || transferredValue != expectedValue { + t.Errorf("Expected destination gomap to contain 'orange': %d after transfer, but it contains '%d'", expectedValue, transferredValue) + } + + // Verify that newMap2 is empty after transfer. + if newMap2.Length() != 0 { + t.Errorf("Expected source gomap to be empty after transfer, but it has %d items", newMap2.Length()) + } +} + +// TestValues tests Map.Values. +func TestValues(t *testing.T) { + // Test case 1: Values of an empty gomap should be an empty slice. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + values := newMap.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 gomap. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + values = newMap.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 gomap with multiple entries. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + newMap.Add("banana", 15) + values = newMap.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 Map.ValuesFunc. +func TestValuesFunc(t *testing.T) { + // Test case 1: ValuesFunc with an empty gomap and a condition that never satisfies. + newMap := &gomap.Map[string, int]{} // Create an empty gomap. + filterFunc := func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values := newMap.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 gomap and a condition that never satisfies. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 2) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = newMap.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 gomap and a condition that satisfies for some values. + newMap = &gomap.Map[string, int]{} // Create an empty gomap. + newMap.Add("apple", 5) + newMap.Add("orange", 10) + newMap.Add("banana", 15) + filterFunc = func(key string, value int) bool { + return value > 7 // Include values greater than 7 in the result + } + values = newMap.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) + } +} diff --git a/hashtable.go b/hashtable.go deleted file mode 100644 index 477165e..0000000 --- a/hashtable.go +++ /dev/null @@ -1,1030 +0,0 @@ -package hashtable - -import ( - "reflect" - - "github.com/lindsaygelle/slice" -) - -// Hashtable represents a generic hash table that maps keys of type K to values of type V. -type Hashtable[K comparable, V any] map[K]V - -// 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. -// -// // Create a new Hashtable instance. -// 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 -} - -// 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. -// -// // Create a new Hashtable instance. -// 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. 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. -// -// // Create a new Hashtable instance. -// 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 { - hashtable.Add(key, value) - } - } - return hashtable -} - -// 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. -// -// // Create a new Hashtable instance. -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// results := newHashtable.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 { - 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 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// -// // Attempt to add key-value pairs. -// 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 { - hashtable.Add(key, value) - } - return ok -} - -// Contains checks if the given value is present in the hashtable and returns the first key-value pair that matches the value. -// It takes a value as input and returns the key and a boolean indicating whether the value is found in the hashtable. -// If the value is found, it returns the corresponding key and true. If the value is not found, it returns the zero value for the key type and false. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// key, found := newHashtable.Contains(5) // Checks if value 5 is in the hashtable, returns the key ("apple" for example) and true if found, or ("", false) if not found -func (hashtable *Hashtable[K, V]) Contains(value V) (K, bool) { - var k K - var ok bool - hashtable.EachBreak(func(key K, v V) bool { - ok = reflect.DeepEqual(v, value) - if ok { - k = key - } - return !ok - }) - return k, ok -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // 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 -} - -// 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. -// -// // Create a new Hashtable instance. -// 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 exists in the hashtable, -// it is deleted. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // 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) - } - 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. -// -// // Create a new Hashtable instance. -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // 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 { - deletetions.Append(hashtable.DeleteOK(key)) - } - return &deletetions -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // 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 { - if reflect.DeepEqual(v, value) { - hashtable.Delete(key) - } - } - } - return hashtable -} - -// 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. -// -// // Create a new Hashtable instance. -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Iterate over the hashtable and print all key-value pairs. -// newHashtable.Each(func(key string, value int) { -// fmt.Println(key, value) -// }) -// // 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 { - fn(key, value) - return true - }) -} - -// 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. -// -// // Create a new Hashtable instance. -// 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 := newHashtable.EachBreak(func(key string, value int) bool { -// fmt.Println(key, value) -// return key != "banana" // Continue printing until "banana" is encountered. -// }) -// // Output: "apple 5", "banana 3" -func (hashtable *Hashtable[K, V]) EachBreak(fn func(key K, value V) bool) *Hashtable[K, V] { - for key, value := range *hashtable { - if !fn(key, value) { - break - } - } - return hashtable -} - -// EachKey iterates over the keys in the hashtable and applies a function to each key. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Iterate over the hashtable and print each key. -// newHashtable.EachKey(func(key string) { -// fmt.Println(key) -// }) -// // Output: "apple", "banana", "cherry" -func (hashtable *Hashtable[K, V]) EachKey(fn func(key K)) *Hashtable[K, V] { - return hashtable.Each(func(key K, _ V) { - fn(key) - }) -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Iterate over the hashtable keys, print them, and break when "banana" is encountered. -// newHashtable.EachKeyBreak(func(key string) bool { -// fmt.Println(key) -// return key != "banana" -// }) -// // 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 { - return fn(key) - }) -} - -// EachValue iterates over the values in the hashtable and applies a function to each value. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Iterate over the hashtable values and print them. -// newHashtable.EachValue(func(value int) { -// fmt.Println(value) -// }) -// // Output: 5, 3, 8 -func (hashtable *Hashtable[K, V]) EachValue(fn func(value V)) *Hashtable[K, V] { - return hashtable.Each(func(_ K, value V) { - fn(value) - }) -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Iterate over the hashtable values and process them until the value is 3. -// newHashtable.EachValueBreak(func(value int) bool { -// fmt.Println(value) -// return value != 3 -// }) -// // 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 { - return fn(value) - }) -} - -// 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. -// -// // Create a new Hashtable instance. -// ht1 := make(Hashtable[string, int]) -// ht1.Add("apple", 5) -// ht1.Add("orange", 10) -// -// // Create a new Hashtable instance. -// ht2 := make(Hashtable[string, int]) -// ht2.Add("apple", 5) -// ht2.Add("orange", 10) -// -// equal := ht1.Equal(ht2) // Returns true because ht1 and ht2 have the same key-value pairs -func (hashtable *Hashtable[K, V]) Equal(otherHashtable *Hashtable[K, V]) bool { - return hashtable.EqualFunc(otherHashtable, func(a, b V) bool { - return reflect.DeepEqual(a, b) - }) -} - -// EqualFunc checks if the current hashtable is equal to another hashtable based on a provided comparison function. -// It takes another hashtable and a comparison function as input and returns true if the two hashtables are equal according to the function. -// The comparison function takes two values as input and returns true if they are considered equal, false otherwise. -// -// // Create a new Hashtable instance. -// ht1 := make(Hashtable[string, int]) -// ht1.Add("apple", 5) -// ht1.Add("orange", 10) -// -// // Create a new Hashtable instance. -// ht2 := make(Hashtable[string, int]) -// ht2.Add("apple", 5) -// ht2.Add("orange", 11) -// -// equal := ht1.EqualFunc(ht2, func(a, b int) bool { -// return math.Abs(float64(a - b)) <= 1 -// }) // Returns true because the values for "orange" (10 and 11) have a difference of 1, within the allowed range -func (hashtable *Hashtable[K, V]) EqualFunc(otherHashtable *Hashtable[K, V], fn func(a V, b V) bool) bool { - if !hashtable.EqualLength(otherHashtable) { - return false - } - for key, value := range *hashtable { - v, ok := otherHashtable.Get(key) - if !ok || !fn(value, v) { - return false - } - } - return true -} - -// EqualLength checks if the current hashtable has the same length as another hashtable. -// It takes another hashtable as input and returns true if the two hashtables have the same length, false otherwise. -// -// // Create a new Hashtable instance. -// ht1 := make(Hashtable[string, int]) -// ht1.Add("apple", 5) -// ht1.Add("orange", 10) -// -// // Create a new Hashtable instance. -// ht2 := make(Hashtable[string, int]) -// ht2.Add("apple", 5) -// -// equalLength := ht1.EqualLength(ht2) // Returns false because ht1 has a length of 2, while ht2 has a length of 1 -func (hashtable *Hashtable[K, V]) EqualLength(otherHashtable *Hashtable[K, V]) bool { - return hashtable.Length() == otherHashtable.Length() -} - -// Fetch retrieves the value associated with the given key from the hashtable. -// It returns the value if the key is found in the hashtable, and the zero value for the value type if the key is not present. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// value := newHashtable.Fetch("apple") // Returns 5, the value associated with the key "apple" -// value = newHashtable.Fetch("orange") // Returns 0 because "orange" is not in the hashtable -func (hashtable *Hashtable[K, V]) Fetch(key K) V { - value, _ := hashtable.Get(key) - return value -} - -// 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. -// -// // Create a new Hashtable instance. -// 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 -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Create a new hashtable containing key-value pairs where the value is greater than 4. -// filteredHashtable := newHashtable.Filter(func(key string, value int) bool { -// return value > 4 -// }) -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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// // Get values for specific keys. -// values := newHashtable.GetMany("apple", "banana", "orange") -// -// // The resulting values slice: {5, 3} -func (hashtable *Hashtable[K, V]) GetMany(keys ...K) *slice.Slice[V] { - values := &slice.Slice[V]{} - for _, key := range keys { - if value, ok := hashtable.Get(key); ok { - values.Append(value) - } - } - return values -} - -// Has checks if the provided key exists in the hashtable. -// It returns true if the key exists, and false otherwise. -// -// // Create a new Hashtable instance. -// 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 -} - -// 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. -// -// // Create a new Hashtable instance. -// 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 := newHashtable.HasMany(keysToCheck...) -// -// // The resulting boolean slice: {true, false, true} -func (hashtable *Hashtable[K, V]) HasMany(keys ...K) *slice.Slice[bool] { - values := make(slice.Slice[bool], len(keys)) - for i, key := range keys { - if hashtable.Has(key) { - values.Replace(i, true) - } - } - return &values -} - -// Intersection creates a new hashtable containing key-value pairs that exist in both the current hashtable and another hashtable. -// It compares values using reflect.DeepEqual to determine equality between the pairs. -// It takes another hashtable as input and returns a new hashtable containing the intersecting key-value pairs. -// -// // 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("apple", 5) -// ht2.Add("orange", 10) -// -// newHashtable := ht1.Intersection(ht2) // Creates a new hashtable with the pair "apple": 5 -func (hashtable *Hashtable[K, V]) Intersection(otherHashtable *Hashtable[K, V]) *Hashtable[K, V] { - return hashtable.IntersectionFunc(otherHashtable, func(key K, a, b V) bool { - return reflect.DeepEqual(a, b) - }) -} - -// IntersectionFunc creates a new hashtable containing key-value pairs that exist in both the current hashtable and another hashtable. -// It takes another hashtable and a condition function as input and adds key-value pairs from the current hashtable to the new hashtable -// if the condition function evaluates to true for the corresponding key-value pair in the other hashtable. -// -// // Create a new Hashtable instance. -// ht1 := make(Hashtable[string, int]) -// ht1.Add("apple", 5) -// ht1.Add("orange", 8) -// -// // Create a new Hashtable instance. -// ht2 := make(Hashtable[string, int]) -// ht2.Add("orange", 8) -// ht2.Add("banana", 6) -// -// newHashtable := ht1.IntersectionFunc(ht2, func(key string, a, b int) bool { -// return a == b -// }) // Creates a new hashtable with the pair "orange": 8 -func (hashtable *Hashtable[K, V]) IntersectionFunc(otherHashtable *Hashtable[K, V], fn func(key K, a V, b V) bool) *Hashtable[K, V] { - newHashtable := make(Hashtable[K, V], 0) - hashtable.Each(func(key K, value V) { - if v, ok := otherHashtable.Get(key); ok && fn(key, value, v) { - newHashtable.Add(key, value) - } - }) - return &newHashtable -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// empty := newHashtable.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. -// -// // Create a new Hashtable instance. -// 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 := 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) { - keys.Append(key) - }) - return &keys -} - -// KeysFunc applies the provided function to each key in the hashtable and returns a slice containing the keys for which the function returns true. -// -// // Create a new Hashtable instance. -// 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 := newHashtable.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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// newHashtable.Add("cherry", 8) -// -// length := newHashtable.Length() // Result: 3 -func (hashtable *Hashtable[K, V]) Length() int { - return len(*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. -// -// // Create a new Hashtable instance. -// newHashtable := make(hashtable.Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// -// // Create a new hashtable with doubled values. -// newHashtable := newHashtable.Map(func(key string, value int) int { -// return value * 2 -// }) -// // 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 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. -// -// // Create a new Hashtable instance. -// 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 -// }) -// // 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 { - value, ok := fn(key, value) - if !ok { - break - } - newHashtable.Add(key, value) - } - return &newHashtable -} - -// Merge merges all key-value pairs from another hashtable into the current hashtable. -// It takes another hashtable as input and adds all its key-value pairs 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.Merge(ht2) // Adds "orange": 10 to ht1 -func (hashtable *Hashtable[K, V]) Merge(otherHashtable *Hashtable[K, V]) *Hashtable[K, V] { - return hashtable.MergeFunc(otherHashtable, func(key K, value V) bool { return true }) -} - -// MergeFunc merges the key-value pairs from another hashtable into the current hashtable based on a provided condition function. -// It takes another hashtable and a condition function as input and adds key-value pairs from the other hashtable to the current hashtable -// if the condition function evaluates to true for the given key-value pair from the other hashtable. -// -// // Create a new Hashtable instance. -// ht1 := make(Hashtable[string, int]) -// ht1.Add("apple", 5) -// ht1.Add("orange", 10) -// -// // Create a new Hashtable instance. -// ht2 := make(Hashtable[string, int]) -// ht2.Add("orange", 8) -// ht2.Add("banana", 6) -// -// // Condition function to merge pairs where the value in ht2 is greater than 7 -// ht1.MergeFunc(ht2, func(key string, value int) bool { -// return value > 7 -// }) // Adds "orange": 8 to ht1, "banana": 6 does not meet the condition and is not added -func (hashtable *Hashtable[K, V]) MergeFunc(otherHashtable *Hashtable[K, V], fn func(key K, value V) bool) *Hashtable[K, V] { - otherHashtable.Each(func(key K, value V) { - if fn(key, value) { - hashtable.Add(key, value) - } - }) - return hashtable -} - -// MergeMany merges key-value pairs from multiple hashtables into the current hashtable. -// It takes a variadic number of hashtables as input and adds all their key-value pairs 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) -// // Create a new Hashtable instance. -// ht3 := make(Hashtable[string, int]) -// ht3.Add("banana", 7) -// -// ht1.MergeMany(ht2, ht3) // Merges key-value pairs from ht2 and ht3 into ht1 -func (hashtable *Hashtable[K, V]) MergeMany(otherHashtables ...*Hashtable[K, V]) *Hashtable[K, V] { - for _, otherHashtable := range otherHashtables { - hashtable.Merge(otherHashtable) - } - return hashtable -} - -// MergeManyFunc merges key-value pairs from multiple hashtables into the current hashtable based on a provided condition function. -// It takes a slice of hashtables and a condition function as input. For each key-value pair in the other hashtables, -// the function is applied, and if it evaluates to true, the pair is added 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) -// // Create a new Hashtable instance. -// ht3 := make(Hashtable[string, int]) -// ht3.Add("banana", 7) -// -// // Condition function to include pairs from the second hashtable only if the value is greater than 7 -// mergedHashtable := ht1.MergeManyFunc([]*Hashtable[string, int]{ht2, ht3}, func(i int, key string, value int) bool { -// return value > 7 -// }) -func (hashtable *Hashtable[K, V]) MergeManyFunc(otherHashtables []*Hashtable[K, V], fn func(i int, key K, value V) bool) *Hashtable[K, V] { - for i, otherHashtable := range otherHashtables { - hashtable.MergeFunc(otherHashtable, func(key K, value V) bool { - return fn(i, key, value) - }) - } - return hashtable -} - -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// result := newHashtable.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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// results := newHashtable.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 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// removedValue := newHashtable.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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// 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) - } - 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("banana", 3) -// removedValues := newHashtable.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.PopOK(key) - if ok { - values.Append(value) - } - } - return &values -} - -// PopManyFunc removes key-value pairs from the hashtable based on the provided condition function and returns the removed values in a slice. -// It takes a condition function as input and removes key-value pairs for which the function evaluates to true. -// It returns a slice containing the removed values. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("orange", 10) -// -// removedValues := newHashtable.PopManyFunc(func(key string, value int) bool { -// return value > 7 // Remove values greater than 7 -// }) // Returns a slice containing [10] -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) { - removedValue := hashtable.Pop(key) - values.Append(removedValue) - } - }) - 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. -// -// // Create a new Hashtable instance. -// 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 -} - -// 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. -// -// // Create a new Hashtable instance. -// 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. -// -// // Create a new Hashtable instance. -// newHashtable := make(Hashtable[string, int]) -// newHashtable.Add("apple", 5) -// newHashtable.Add("orange", 10) -// values := newHashtable.ValuesFunc(func(key string, value int) bool { -// return value > 7 // Include values greater than 7 in the result -// }) // 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 deleted file mode 100644 index e47849f..0000000 --- a/hashtable_test.go +++ /dev/null @@ -1,1652 +0,0 @@ -package hashtable_test - -import ( - "math" - "reflect" - "sort" - "strings" - "testing" - - "github.com/lindsaygelle/hashtable" - "github.com/lindsaygelle/slice" -) - -// TestHashtable tests Hashtable. -func TestHashtable(t *testing.T) { - table := hashtable.Hashtable[string, string]{ - "hello": "world"} - t.Log(table) -} - -// TestAdd tests Hashtable.Add. -func TestAdd(t *testing.T) { - // 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) - - // 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) - } - - // 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 { - t.Errorf("Expected key 'banana' to have updated value 10, but got %v", value) - } - - // 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 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) - } -} - -// TestAddLength tests Hashtable.AddLength. -func TestAddLength(t *testing.T) { - // 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) - } - - // 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) - } - - // Test case 3: Add a new key-value pair. - length = ht.AddLength("banana", 3) - if length != 2 { - t.Errorf("Expected length of hashtable after adding 'banana' with value 3 to be 2, but got %v", length) - } - - // 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 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) { - // 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}) - - // 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) - } - } - - // 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. - 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 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) - } - } - - // 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) { - // 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}, - map[string]int{"banana": 3}, - map[string]int{"banana": 10}, - map[string]int{"cherry": 8}, - ) - - // 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 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) - } - } - - // Test case 2: Add new key-value pairs. - results = ht.AddManyOK( - map[string]int{"watermelon": 12, "pineapple": 7}, - ) - - // 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 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) - } - } -} - -// TestAddOK tests Hashtable.AddOK. -func TestAddOK(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - - // Test adding a new key-value pair. - added := ht.AddOK("apple", 5) - if !added { - t.Error("Expected 'apple' to be added, but it was not.") - } - - // Check if the key-value pair is added correctly. - value, exists := ht["apple"] - if !exists || value != 5 { - t.Fatalf("Expected key 'apple' with value '5', but got value '%d'", value) - } - - // Test adding an existing key. - reAdded := ht.AddOK("apple", 10) - if reAdded { - t.Error("Expected 'apple' to not be re-added, but it was.") - } - - // Check if the value for 'apple' remains unchanged. - value, exists = ht["apple"] - if !exists || value != 5 { - t.Fatalf("Expected key 'apple' to have value '5' after re-adding attempt, but got value '%d'", value) - } - - // Test adding another new key-value pair. - addedNew := ht.AddOK("banana", 3) - if !addedNew { - t.Error("Expected 'banana' to be added, but it was not.") - } - - // Check if the key-value pair for 'banana' is added correctly. - value, exists = ht["banana"] - if !exists || value != 3 { - t.Fatalf("Expected key 'banana' with value '3', but got value '%d'", value) - } -} - -// TestContains tests Hashtable.Contains. -func TestContains(t *testing.T) { - // Test case 1: Check for a value in an empty hashtable. - ht := &hashtable.Hashtable[string, int]{} // Create an empty hashtable. - key, found := ht.Contains(5) // Check if value 5 is in the hashtable. - - // Since the hashtable is empty, the result should be ("", false). - if key != "" || found { - t.Errorf("Expected ('', false), but got (%s, %t)", key, found) - } - - // Test case 2: Check for a value in a non-empty hashtable. - ht = &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht.Add("apple", 5) - ht.Add("banana", 10) - ht.Add("orange", 5) - - // Check if value 5 is in the hashtable. - key, found = ht.Contains(5) - - // Since value 5 exists in the hashtable, the result should be ("apple", true). - if !found { - t.Errorf("Expected ('%s', true), but got (%s, %t)", key, key, found) - } - - // Test case 3: Check for a value that doesn't exist in the hashtable. - key, found = ht.Contains(15) // Check if value 15 is in the hashtable. - - // Since value 15 doesn't exist in the hashtable, the result should be ("", false). - if key != "" || found { - t.Errorf("Expected ('', false), but got (%s, %t)", key, found) - } -} - -// TestDelete tests Hashtable.Delete. -func TestDelete(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["banana"] = 3 - - // Test case 1: Delete an existing key. - ht.Delete("apple") - if _, ok := ht["apple"]; ok { - t.Fatalf("Expected key 'apple' to be deleted, but it still exists in the hashtable") - } - - // Test case 2: Delete a non-existing key. - ht.Delete("nonexistent") - if _, ok := ht["nonexistent"]; ok { - t.Fatalf("Expected key 'nonexistent' to not exist, but it was found in the hashtable") - } - - // Test case 3: Delete a key after adding it again. - ht["apple"] = 10 - ht.Delete("apple") - if _, ok := ht["apple"]; ok { - t.Fatalf("Expected key 'apple' to be deleted, but it still exists in the hashtable") - } -} - -// TestDeleteLength tests Hashtable.DeleteLength. -func TestDeleteLength(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - - // Add key-value pairs to the hashtable and get the initial length. - ht["apple"] = 5 - ht["banana"] = 3 - initialLength := len(ht) - - // Delete an existing key from the hashtable and get the updated length. - lengthAfterDelete := ht.DeleteLength("apple") - - // Expected length after deleting "apple": initial length - 1. - expectedLength := initialLength - 1 - - // Verify that the obtained length matches the expected length. - if lengthAfterDelete != expectedLength { - t.Fatalf("Expected length: %d, but got: %d", expectedLength, lengthAfterDelete) - } - - // Attempt to delete a non-existing key from the hashtable. - lengthAfterNonExistingDelete := ht.DeleteLength("grape") - - // Length should remain the same after attempting to delete a non-existing key. - // Expected length: initial length. - expectedLengthNonExisting := len(ht) - - // Verify that the obtained length matches the expected length. - if lengthAfterNonExistingDelete != expectedLengthNonExisting { - t.Fatalf("Expected length: %d, but got: %d", expectedLengthNonExisting, lengthAfterNonExistingDelete) - } -} - -// TestDeleteMany tests Hashtable.DeleteMany. -func TestDeleteMany(t *testing.T) { - // 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". - - // The hashtable should remain empty. - if !ht.IsEmpty() { - t.Errorf("Expected hashtable to be empty, but got non-empty hashtable: %v", ht) - } - - // 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) - - // 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 !reflect.DeepEqual(expected, result) { - t.Errorf("Expected keys %v after deleting 'apple' and 'banana', but got keys %v", expected, result) - } -} - -// 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.Fatalf("Expected deletion of key %s to be %v but got %v", keysToDelete[i], expectedResults[i], result) - } - } -} - -// 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. - 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.Fatalf("Expected deletion of 'apple' to be successful") - } - - // Attempt to delete a key that does not exist. - notDeleted := ht.DeleteOK("grape") - if !notDeleted { - 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.Fatalf("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]) - - // Add key-value pairs to the hashtable. - ht["apple"] = 5 - ht["banana"] = 3 - ht["cherry"] = 8 - - // Define a map to store the expected output. - expectedOutput := map[string]int{ - "apple": 5, - "banana": 3, - "cherry": 8, - } - - // Define a function to compare the actual output with the expected output. - printKeyValue := func(key string, value int) { - if expected, ok := expectedOutput[key]; ok { - if value != expected { - t.Fatalf("Expected %s: %d, but got %d", key, expected, value) - } - delete(expectedOutput, key) - } else { - t.Fatalf("Unexpected key: %s", key) - } - } - - // Call the Each function with the printKeyValue function. - ht.Each(printKeyValue) - - // Check if all expected keys have been processed. - if len(expectedOutput) > 0 { - t.Fatalf("Not all keys were processed: %v", expectedOutput) - } -} - -// TestEachBreak tests Hashtable.EachBreak. -func TestEachBreak(t *testing.T) { - var stopPrinting string - ht := make(hashtable.Hashtable[string, int]) - - // Add key-value pairs to the hashtable. - ht.Add("apple", 5) - ht.Add("banana", 3) - ht.Add("cherry", 8) - - // Define a function to stop iteration when key is "banana". - ht.EachBreak(func(key string, value int) bool { - t.Logf("%s %d", key, value) - stopPrinting = key - return key != "banana" - }) - - // Ensure that iteration stopped at "banana". - if stopPrinting != "banana" { - t.Fatalf("Iteration did not stop at 'banana'.") - } -} - -// TestEachKey tests Hashtable.EachKey. -func TestEachKey(t *testing.T) { - 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 print each key. - var printedKeys []string - printKey := func(key string) { - printedKeys = append(printedKeys, key) - } - - // Iterate over the keys and print each key. - ht.EachKey(printKey) - - // Sort the printed values for consistent comparison. - sort.Strings(printedKeys) - - // Expected output: "apple", "banana", "cherry". - expectedKeys := []string{"apple", "banana", "cherry"} - for i, key := range printedKeys { - if key != expectedKeys[i] { - t.Fatalf("Expected key %s at index %d, but got %s", expectedKeys[i], i, key) - } - } -} - -// TestEachKeyBreak tests Hashtable.EachKeyBreak. -func TestEachKeyBreak(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - - // Add key-value pairs to the hashtable. - ht["apple"] = 5 - ht["banana"] = 3 - ht["cherry"] = 8 - - var keyToBreak string - ht.EachBreak(func(key string, value int) bool { - keyToBreak = key - return key != "banana" - }) - - if keyToBreak != "banana" { - t.Fatalf("Expect keyToBreak to equal 'banana', but got %s", keyToBreak) - } -} - -// TestEachValue tests Hashtable.EachValue. -func TestEachValue(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 print each value. - var printedValues []int - printValue := func(value int) { - printedValues = append(printedValues, value) - } - - // Iterate over the hashtable values and print them. - ht.EachValue(printValue) - - // Sort the printed values for consistent comparison. - sort.Ints(printedValues) - - // Expected output: 3, 5, 8. - expectedValues := []int{3, 5, 8} - - if len(printedValues) != len(expectedValues) { - t.Fatalf("Expected %d values, but got %d", len(expectedValues), len(printedValues)) - return - } - - for i, value := range printedValues { - if value != expectedValues[i] { - t.Fatalf("Expected value %d at index %d, but got %d", expectedValues[i], i, value) - } - } -} - -// TestEachValueBreak tests Hashtable.EachValueBreak. - -func TestEachValueBreak(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) - ht.Add("cherry", 8) - - 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. - var processedValues []int - processValue := func(value int) bool { - processedValues = append(processedValues, value) - return value != 3 - } - - // Iterate over the hashtable values and process them until the value is 3. - for _, key := range keys { - value, _ := ht.Get(key) - if !processValue(value) { - break - } - } - - // Expected output: 5, 3. - expectedValues := []int{5, 3} - for i, value := range processedValues { - if value != expectedValues[i] { - t.Fatalf("Expected value %d at index %d, but got %d", expectedValues[i], i, value) - } - } -} - -// 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. - ht1 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht1.Add("apple", 5) - ht1.Add("orange", 10) - - ht2 := &hashtable.Hashtable[string, int]{} // Create another hashtable with similar values. - ht2.Add("apple", 5) - ht2.Add("orange", 10) - - // Check if the two hashtables are equal. - equal := ht1.Equal(ht2) - - // Since ht1 and ht2 have the same key-value pairs, they are considered equal. - if !equal { - t.Errorf("Expected true, but got false") - } - - // Test case 2: Compare unequal hashtables. - ht3 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht3.Add("apple", 5) - ht3.Add("orange", 10) - - ht4 := &hashtable.Hashtable[string, int]{} // Create another hashtable with different values for "orange". - ht4.Add("apple", 5) - ht4.Add("orange", 12) - - // Check if the two hashtables are equal. - equal = ht3.Equal(ht4) - - // Since ht3 and ht4 have different values for "orange", they are not considered equal. - if equal { - t.Errorf("Expected false, but got true") - } -} - -// TestEqualFunc tests Hashtable.EqualFunc. -func TestEqualFunc(t *testing.T) { - // Custom comparison function to check if two integers are equal when their difference is less than or equal to 1 - compareFunc := func(a, b int) bool { - return math.Abs(float64(a-b)) <= 1 - } - - // Test case 1: Compare equal hashtables based on the custom comparison function. - ht1 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht1.Add("apple", 5) - ht1.Add("orange", 10) - - ht2 := &hashtable.Hashtable[string, int]{} // Create another hashtable with similar values. - ht2.Add("apple", 5) - ht2.Add("orange", 11) // The difference between 10 and 11 is within the allowed range according to compareFunc. - - // Check if the two hashtables are equal based on the custom comparison function. - equal := ht1.EqualFunc(ht2, compareFunc) - - // Since the values for "orange" (10 and 11) have a difference of 1, within the allowed range, - // the hashtables are considered equal according to the custom comparison function. - if !equal { - t.Errorf("Expected true, but got false") - } - - // Test case 2: Compare unequal hashtables based on the custom comparison function. - ht3 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht3.Add("apple", 5) - ht3.Add("orange", 10) - - ht4 := &hashtable.Hashtable[string, int]{} // Create another hashtable with different values for "orange". - ht4.Add("apple", 5) - ht4.Add("orange", 12) // The difference between 10 and 12 is greater than the allowed range according to compareFunc. - - // Check if the two hashtables are equal based on the custom comparison function. - equal = ht3.EqualFunc(ht4, compareFunc) - - // Since the difference between 10 and 12 is greater than the allowed range according to compareFunc, - // the hashtables are not considered equal based on the custom comparison function. - if equal { - t.Errorf("Expected false, but got true") - } -} - -// TestEqualLength tests Hashtable.EqualLength. -func TestEqualLength(t *testing.T) { - // Test case 1: Compare hashtables with equal length. - ht1 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht1.Add("apple", 5) - ht1.Add("orange", 10) - - ht2 := &hashtable.Hashtable[string, int]{} // Create another hashtable with the same number of key-value pairs. - ht2.Add("apple", 5) - ht2.Add("orange", 7) - - // Check if the two hashtables have equal length. - equalLength := ht1.EqualLength(ht2) - - // Since ht1 and ht2 have the same number of key-value pairs, they are considered equal in length. - if !equalLength { - t.Errorf("Expected true, but got false") - } - - // Test case 2: Compare hashtables with unequal length. - ht3 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht3.Add("apple", 5) - - ht4 := &hashtable.Hashtable[string, int]{} // Create another hashtable with a different number of key-value pairs. - ht4.Add("apple", 5) - ht4.Add("orange", 7) - - // Check if the two hashtables have equal length. - equalLength = ht3.EqualLength(ht4) - - // Since ht3 and ht4 have different numbers of key-value pairs, they are not considered equal in length. - if equalLength { - t.Errorf("Expected false, but got true") - } -} - -// TestFetch tests Hashtable.Fetch. -func TestFetch(t *testing.T) { - // Test case 1: Fetch value for an existing key. - ht := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht.Add("apple", 5) - ht.Add("orange", 10) - - // Fetch the value associated with the key "apple". - fetchedValue := ht.Fetch("apple") - - // Since "apple" is in the hashtable, the fetched value should be 5. - expectedValue := 5 - if fetchedValue != expectedValue { - t.Errorf("Expected %d, but got %d", expectedValue, fetchedValue) - } - - // Test case 2: Fetch value for a non-existing key. - // Fetch the value associated with the key "banana", which is not in the hashtable. - fetchedValue = ht.Fetch("banana") - - // Since "banana" is not in the hashtable, the fetched value should be the zero value for int, which is 0. - expectedValue = 0 - if fetchedValue != expectedValue { - t.Errorf("Expected %d, but got %d", expectedValue, fetchedValue) - } -} - -// 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) { - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["banana"] = 3 - - // Test case 1: Get an existing key. - value, exists := ht.Get("apple") - if !exists { - t.Fatalf("Expected key 'apple' to exist, but it was not found in the hashtable") - } - if value != 5 { - t.Fatalf("Expected value for key 'apple' to be 5, but got %d", value) - } - - // Test case 2: Get a non-existing key. - value, exists = ht.Get("orange") - if exists { - t.Fatalf("Expected key 'orange' to not exist, but it was found in the hashtable with value %d", value) - } - if value != 0 { - t.Fatalf("Expected default value for non-existing key 'orange' to be 0, but got %d", value) - } -} - -// TestGetMany tests Hashtable.GetMany. -func TestGetMany(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["banana"] = 3 - ht["cherry"] = 8 - - // Get values for specific keys. - values := ht.GetMany("apple", "banana", "orange") - - // Sort the keys for consistent iteration order. - sort.Ints(*values) - - // The expected values slice: {5, 3}. - expectedValues := &slice.Slice[int]{5, 3} - - // Sort the keys for consistent iteration order. - sort.Ints(*expectedValues) - - // Verify that the obtained values match the expected values. - if values == expectedValues { - t.Fatalf("Expected values: %v, but got: %v", expectedValues, values) - } -} - -// TestHas tests Hashtable.Has. - -func TestHas(t *testing.T) { - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["banana"] = 3 - - // Test case 1: Key exists in the hashtable. - if !ht.Has("apple") { - t.Fatalf("Expected key 'apple' to exist, but it was not found in the hashtable") - } - - // Test case 2: Key does not exist in the hashtable. - if ht.Has("orange") { - t.Fatalf("Expected key 'orange' to not exist, but it was found in the hashtable") - } -} - -// TestHasMany test Hashtable.HasMany. -func TestHasMany(t *testing.T) { - // Create a new hashtable. - ht := make(hashtable.Hashtable[string, int]) - ht["apple"] = 5 - ht["banana"] = 3 - ht["cherry"] = 8 - - // Keys to check existence. - keysToCheck := []string{"apple", "orange", "banana"} - - // Check the existence of multiple keys. - results := ht.HasMany(keysToCheck...) - - // The expected boolean slice: {true, false, true}. - expectedResults := &slice.Slice[bool]{true, false, true} - - // Verify that the obtained results match the expected results. - if !reflect.DeepEqual(results, expectedResults) { - t.Fatalf("Expected results: %v, but got: %v", expectedResults, results) - } -} - -// TestIntersectionFunc tests Hashtable.IntersectionFunc. -func TestIntersectionFunc(t *testing.T) { - // Test case: Check intersection of two hashtables with common key-value pairs. - ht1 := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 8, - } - ht2 := &hashtable.Hashtable[string, int]{ - "orange": 8, - "banana": 6, - } - - // Condition function to check if values are equal. - conditionFunc := func(key string, a, b int) bool { - return a == b - } - - newHashtable := ht1.IntersectionFunc(ht2, conditionFunc) - - expectedHashtable := &hashtable.Hashtable[string, int]{ - "orange": 8, - } - - if !reflect.DeepEqual(expectedHashtable, newHashtable) { - t.Errorf("Expected intersection result to be %v, but got %v", expectedHashtable, newHashtable) - } - - // Test case: Check intersection of two hashtables with no common key-value pairs. - ht1 = &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 8, - } - ht2 = &hashtable.Hashtable[string, int]{ - "banana": 10, - "grape": 7, - } - - newHashtable = ht1.IntersectionFunc(ht2, conditionFunc) - - expectedHashtable = &hashtable.Hashtable[string, int]{} - - if !reflect.DeepEqual(expectedHashtable, newHashtable) { - t.Errorf("Expected intersection result to be %v, but got %v", expectedHashtable, newHashtable) - } - - // Test case: Check intersection of empty hashtables. - ht1 = &hashtable.Hashtable[string, int]{} - ht2 = &hashtable.Hashtable[string, int]{} - - newHashtable = ht1.IntersectionFunc(ht2, conditionFunc) - - expectedHashtable = &hashtable.Hashtable[string, int]{} - - if !reflect.DeepEqual(expectedHashtable, ht1) { - t.Errorf("Expected intersection result to be %v, but got %v", expectedHashtable, newHashtable) - } -} - -// 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.Fatalf("Expected keys: %v, but got: %v", expectedKeys, keys) - } -} - -// TestKeysFunc tests Hashtable.Keys. -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.Fatalf("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.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) - } - } -} - -// TestMerge tests Hashtable.Merge. -func TestMerge(t *testing.T) { - // Test case: Merge all key-value pairs from another hashtable. - ht1 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht1.Add("apple", 5) - - ht2 := &hashtable.Hashtable[string, int]{} // Create another hashtable. - ht2.Add("orange", 10) - - // Merge all key-value pairs from ht2 into ht1. - ht1.Merge(ht2) - - // After merging, ht1 should contain: {"apple": 5, "orange": 10} - expectedHashtable := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 10, - } - - // Verify that ht1 is equal to the expected hashtable. - if !reflect.DeepEqual(expectedHashtable, ht1) { - t.Errorf("Merge did not produce the expected result. Got: %v, Expected: %v", ht1, expectedHashtable) - } -} - -// TestMergeFunc tests Hashtable.MergeFunc. -func TestMergeFunc(t *testing.T) { - // Test case: Merge key-value pairs based on the condition function. - ht1 := &hashtable.Hashtable[string, int]{} // Create a new hashtable. - ht1.Add("apple", 5) - ht1.Add("orange", 10) - - ht2 := &hashtable.Hashtable[string, int]{} // Create another hashtable. - ht2.Add("orange", 8) - ht2.Add("banana", 6) - - // Condition function to merge pairs where the value in ht2 is greater than 7. - conditionFunc := func(key string, value int) bool { - return value > 7 - } - - // Merge key-value pairs from ht2 into ht1 based on the condition function. - ht1.MergeFunc(ht2, conditionFunc) - - // After merging, ht1 should contain: {"apple": 5, "orange": 8} - expectedHashtable := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 8, - } - - // Verify that ht1 is equal to the expected hashtable. - if !ht1.Equal(expectedHashtable) { - t.Errorf("MergeFunc did not produce the expected result. Got: %v, Expected: %v", ht1, expectedHashtable) - } -} - -// TestMergeMany tests Hashtable.MergeMany. -func TestMergeMany(t *testing.T) { - // Create hashtables for merging. - ht1 := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 10, - } - ht2 := &hashtable.Hashtable[string, int]{ - "orange": 15, - "banana": 7, - } - ht3 := &hashtable.Hashtable[string, int]{ - "grape": 8, - "melon": 12, - } - - // Merge key-value pairs from ht2 and ht3 into ht1. - ht1.MergeMany(ht2, ht3) - - // Expected merged hashtable. - expectedHashtable := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 15, - "banana": 7, - "grape": 8, - "melon": 12, - } - - // Check if the merged hashtable matches the expected hashtable. - if !reflect.DeepEqual(expectedHashtable, ht1) { - t.Errorf("Merged hashtable does not match the expected hashtable. Got: %v, Expected: %v", ht1, expectedHashtable) - } - - // Test case for merging an empty hashtable. - emptyHashtable := &hashtable.Hashtable[string, int]{} - ht1.MergeMany(emptyHashtable) - - // Merged hashtable should remain unchanged. - if !reflect.DeepEqual(expectedHashtable, ht1) { - t.Errorf("Merged hashtable should remain unchanged after merging with an empty hashtable. Got: %v, Expected: %v", ht1, expectedHashtable) - } -} - -// TestMergeManyFunc tests Hashtable.MergeManyFunc. -func TestMergeManyFunc(t *testing.T) { - // Test case: Merge key-value pairs based on a condition function. - ht1 := &hashtable.Hashtable[string, int]{} // Create an empty destination hashtable. - ht2 := &hashtable.Hashtable[string, int]{} // Create the first source hashtable. - ht2.Add("apple", 5) - ht3 := &hashtable.Hashtable[string, int]{} // Create the second source hashtable. - ht3.Add("orange", 10) - ht4 := &hashtable.Hashtable[string, int]{} // Create the third source hashtable. - ht4.Add("banana", 7) - - // Condition function to include pairs only if the value is greater than 7. - conditionFunc := func(i int, key string, value int) bool { - return value >= 7 - } - - // Merge key-value pairs based on the condition function. - mergedHashtable := ht1.MergeManyFunc([]*hashtable.Hashtable[string, int]{ht2, ht3, ht4}, conditionFunc) - - // Verify that the merged hashtable contains the expected key-value pairs. - expectedPairs := map[string]int{ - "orange": 10, - "banana": 7, - } - for key, expectedValue := range expectedPairs { - value, ok := mergedHashtable.Get(key) - if !ok || value != expectedValue { - t.Errorf("Expected merged hashtable to contain key '%s': %d, but it contains '%d'", key, expectedValue, value) - } - } - - // Verify that unwanted pairs are not present in the merged hashtable. - unwantedPairs := map[string]int{ - "apple": 5, - } - for key := range unwantedPairs { - _, ok := mergedHashtable.Get(key) - if ok { - t.Errorf("Expected merged hashtable not to contain key '%s', but it was found", key) - } - } -} - -// 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 := 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) - } - - // 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.PopOK("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.PopOK("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") - } -} - -// TestPopManyFunc tests Hashtable.PopManyFunc. -func TestPopManyFunc(t *testing.T) { - // Test case 1: Pop values greater than 7 from the hashtable. - ht := &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 10, - "banana": 8, - "grape": 12, - } - - removeCondition := func(key string, value int) bool { - return value > 7 - } - - removedValues := ht.PopManyFunc(removeCondition) - - sort.Ints(*removedValues) - - expectedRemovedValues := &slice.Slice[int]{8, 10, 12} - - if !reflect.DeepEqual(expectedRemovedValues, removedValues) { - t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) - } - - // Test case 2: Pop values when condition does not match any key-value pairs. - ht = &hashtable.Hashtable[string, int]{ - "apple": 5, - "orange": 3, - "banana": 8, - } - - removeCondition = func(key string, value int) bool { - return value > 10 - } - - removedValues = ht.PopManyFunc(removeCondition) - - expectedRemovedValues = &slice.Slice[int]{} // No values match the condition. - - if !reflect.DeepEqual(expectedRemovedValues, removedValues) { - t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) - } - - // Test case 3: Pop values from an empty hashtable. - ht = &hashtable.Hashtable[string, int]{} - - removeCondition = func(key string, value int) bool { - return value > 0 - } - - removedValues = ht.PopManyFunc(removeCondition) - - expectedRemovedValues = &slice.Slice[int]{} // No values to remove from an empty hashtable. - - if !reflect.DeepEqual(expectedRemovedValues, removedValues) { - t.Errorf("Expected removed values to be %v, but got %v", expectedRemovedValues, removedValues) - } -} - -// 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) - } -} - -// 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. - 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) - } -}