├── go.sum ├── go.mod ├── example_slice_test.go ├── .github └── workflows │ └── go.yml ├── LICENSE ├── map.go ├── map_test.go ├── README.md ├── set.go ├── slice.go ├── slice_test.go └── set_test.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/emad-elsaid/types 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /example_slice_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "fmt" 4 | 5 | func ExampleSlice() { 6 | a := Slice[int]{1, 2, 3, 4, 5, 6} 7 | 8 | // multiply every element by 100 9 | a = a.Map(func(e int) int { 10 | return e * 100 11 | }) 12 | 13 | // select any element <= 300 14 | a = a.KeepIf(func(e int) bool { 15 | return e <= 300 16 | }) 17 | 18 | fmt.Print(a) 19 | 20 | // Output: [100 200 300] 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.22 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Emad Elsaid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "sync" 4 | 5 | // Map is a wrapper for sync.Map with same methods (except for CompareAnd*) 6 | // Check: https://pkg.go.dev/sync#Map 7 | type Map[K comparable, V any] struct { 8 | store sync.Map 9 | } 10 | 11 | func (m *Map[K, V]) Store(k K, v V) { 12 | m.store.Store(k, v) 13 | } 14 | 15 | func (m *Map[K, V]) Load(k K) (v V, ok bool) { 16 | var a any 17 | 18 | a, ok = m.store.Load(k) 19 | if ok { 20 | v = a.(V) 21 | } 22 | 23 | return 24 | } 25 | 26 | // Delete see sync.Map#Delete 27 | func (m *Map[K, V]) Delete(k K) { 28 | m.store.Delete(k) 29 | } 30 | 31 | // Range see sync.Map#Range 32 | func (m *Map[K, V]) Range(f func(k K, v V) bool) { 33 | m.store.Range(func(k, v any) bool { 34 | return f(k.(K), v.(V)) 35 | }) 36 | } 37 | 38 | // LoadAndDelete see sync.Map#LoadAndDelete 39 | func (m *Map[K, V]) LoadAndDelete(k K) (v V, loaded bool) { 40 | var a any 41 | a, loaded = m.store.LoadAndDelete(k) 42 | if loaded { 43 | v = a.(V) 44 | } 45 | 46 | return 47 | } 48 | 49 | // LoadOrStore see sync.Map#LoadOrStore 50 | func (m *Map[K, V]) LoadOrStore(k K, v V) (actual V, loaded bool) { 51 | var a any 52 | a, loaded = m.store.LoadOrStore(k, v) 53 | actual = a.(V) 54 | 55 | return 56 | } 57 | 58 | // Swap see sync.Map#Swap 59 | func (m *Map[K, V]) Swap(k K, v V) (previous V, loaded bool) { 60 | var a any 61 | a, loaded = m.store.Swap(k, v) 62 | previous, _ = a.(V) 63 | 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /map_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "testing" 4 | 5 | func TestMap(t *testing.T) { 6 | t.Run("Store/Load", func(t *testing.T) { 7 | m := Map[string, string]{} 8 | m.Store("Key1", "Value1") 9 | m.Store("Key2", "Value2") 10 | 11 | if v, ok := m.Load("Key1"); v != "Value1" || !ok { 12 | t.Errorf("Expected (%s, %t) but got (%s,%t)", v, ok, v, ok) 13 | } 14 | 15 | if v, ok := m.Load("Key2"); v != "Value2" || !ok { 16 | t.Errorf("Expected (%s, %t) but got (%s,%t)", v, ok, v, ok) 17 | } 18 | 19 | if v, ok := m.Load("Key3"); v != "" || ok { 20 | t.Errorf("Expected (%s, %t) but got (%s,%t)", v, ok, v, ok) 21 | } 22 | }) 23 | 24 | t.Run("Delete", func(t *testing.T) { 25 | m := Map[string, string]{} 26 | m.Store("Key1", "Value1") 27 | m.Delete("Key1") 28 | 29 | if v, ok := m.Load("Key1"); ok { 30 | t.Errorf("Key shouldn't exist but it was found: %v", v) 31 | } 32 | }) 33 | 34 | t.Run("Range", func(t *testing.T) { 35 | m := Map[string, string]{} 36 | m.Store("Key1", "Value1") 37 | m.Store("Key2", "Value2") 38 | 39 | cpy := Map[string, string]{} 40 | m.Range(func(k string, v string) bool { 41 | cpy.Store(k, v) 42 | return true 43 | }) 44 | 45 | if v, ok := cpy.Load("Key1"); v != "Value1" || !ok { 46 | t.Errorf("Expected: Value1, got: %s", v) 47 | } 48 | 49 | if v, ok := cpy.Load("Key2"); v != "Value2" || !ok { 50 | t.Errorf("Expected: Value2, got: %s", v) 51 | } 52 | }) 53 | 54 | t.Run("LoadAndDelete", func(t *testing.T) { 55 | m := Map[string, string]{} 56 | m.Store("Key1", "Value1") 57 | 58 | if v, loaded := m.LoadAndDelete("Key1"); v != "Value1" || !loaded { 59 | t.Errorf("Expected: Value1 and true, got: %s, %t", v, loaded) 60 | } 61 | 62 | if v, loaded := m.LoadAndDelete("Key2"); v != "" || loaded { 63 | t.Errorf("Expected: empty and false, got: %s, %t", v, loaded) 64 | } 65 | }) 66 | 67 | t.Run("LoadOrStore", func(t *testing.T) { 68 | m := Map[string, string]{} 69 | m.Store("Key1", "Value1") 70 | 71 | if v, loaded := m.LoadOrStore("Key1", "Value1/1"); v != "Value1" || !loaded { 72 | t.Errorf("Expected Value1 and loaded, got: %s, %t", v, loaded) 73 | } 74 | 75 | if v, loaded := m.LoadOrStore("Key2", "Value2"); v != "Value2" || loaded { 76 | t.Errorf("Expected Value2 and not loaded, got: %s, %t", v, loaded) 77 | } 78 | }) 79 | 80 | t.Run("Swap", func(t *testing.T) { 81 | m := Map[string, string]{} 82 | m.Store("Key1", "Value1") 83 | 84 | if v, loaded := m.Swap("Key1", "Value1/1"); v != "Value1" || !loaded { 85 | t.Errorf("Expected Value1 and loaded, got: %s, %t", v, loaded) 86 | } 87 | 88 | if v, loaded := m.Swap("Key2", "Value2"); v != "" || loaded { 89 | t.Errorf("Expected Value2 and not loaded, got: %s, %t", v, loaded) 90 | } 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/emad-elsaid/types)](https://goreportcard.com/report/github.com/emad-elsaid/types) 4 | [![GoDoc](https://godoc.org/github.com/emad-elsaid/types?status.svg)](https://godoc.org/github.com/emad-elsaid/types) 5 | [![codecov](https://codecov.io/gh/emad-elsaid/types/branch/master/graph/badge.svg)](https://codecov.io/gh/emad-elsaid/types) 6 | 7 | Go implementation for generic types, imitating Ruby types 8 | 9 | # Data structures 10 | 11 | ## Slice 12 | 13 | A slice of `comparable` type that can hold any value 14 | 15 | ### Example 16 | 17 | ```go 18 | func ExampleSlice() { 19 | a := Slice[int]{1, 2, 3, 4, 5, 6} 20 | 21 | // multiply every element by 100 22 | a = a.Map(func(e int) int { 23 | return e * 100 24 | }) 25 | 26 | // select any element <= 300 27 | a = a.KeepIf(func(e int) bool { 28 | return e <= 300 29 | }) 30 | 31 | fmt.Print(a) 32 | 33 | // Output: [100 200 300] 34 | } 35 | ``` 36 | 37 | ### Slice Methods available: 38 | 39 | ```go 40 | func (a Slice[T]) All(block func(T) bool) bool 41 | func (a Slice[T]) Any(block func(T) bool) bool 42 | func (a Slice[T]) At(index int) *T 43 | func (a Slice[T]) CountBy(block func(T) bool) (count int) 44 | func (a Slice[T]) CountElement(element T) (count int) 45 | func (a Slice[T]) Cycle(count int, block func(T)) 46 | func (a Slice[T]) Delete(element T) Slice[T] 47 | func (a Slice[T]) DeleteAt(index int) Slice[T] 48 | func (a Slice[T]) DeleteIf(block func(T) bool) Slice[T] 49 | func (a Slice[T]) Drop(count int) Slice[T] 50 | func (a Slice[T]) Each(block func(T)) 51 | func (a Slice[T]) EachIndex(block func(int)) 52 | func (a Slice[T]) Fetch(index int, defaultValue T) T 53 | func (a Slice[T]) Fill(element T, start int, length int) Slice[T] 54 | func (a Slice[T]) FillWith(start int, length int, block func(int) T) Slice[T] 55 | func (a Slice[T]) First() *T 56 | func (a Slice[T]) Firsts(count int) Slice[T] 57 | func (a Slice[T]) Include(element T) bool 58 | func (a Slice[T]) Index(element T) int 59 | func (a Slice[T]) IndexBy(block func(T) bool) int 60 | func (a Slice[T]) Insert(index int, elements ...T) Slice[T] 61 | func (a Slice[T]) IsEmpty() bool 62 | func (a Slice[T]) IsEq(other Slice[T]) bool 63 | func (a Slice[T]) KeepIf(block func(T) bool) Slice[T] 64 | func (a Slice[T]) Last() *T 65 | func (a Slice[T]) Lasts(count int) Slice[T] 66 | func (a Slice[T]) Len() int 67 | func (a Slice[T]) Map(block func(T) T) Slice[T] 68 | func (a Slice[T]) Max(block func(T) int) T 69 | func (a Slice[T]) Min(block func(T) int) T 70 | func (a Slice[T]) Partition(predicate func(T) bool) (Slice[T], Slice[T]) 71 | func (a Slice[T]) Pop() (Slice[T], T) 72 | func (a Slice[T]) Push(element T) Slice[T] 73 | func (a Slice[T]) Reduce(block func(T) bool) Slice[T] 74 | func (a Slice[T]) Reverse() Slice[T] 75 | func (a Slice[T]) Select(block func(T) bool) Slice[T] 76 | func (a Slice[T]) SelectUntil(block func(T) bool) Slice[T] 77 | func (a Slice[T]) Shift() (T, Slice[T]) 78 | func (a Slice[T]) Shuffle() Slice[T] 79 | func (a Slice[T]) Unique() Slice[T] 80 | func (a Slice[T]) Unshift(element T) Slice[T] 81 | func SliceReduce[T comparable, U any](s Slice[T], initial U, fn func(U, T) U) U 82 | ``` 83 | 84 | ## Set 85 | 86 | An order-preserving set of `comparable` type that stores unique elements 87 | 88 | ### Example 89 | 90 | ```go 91 | func ExampleSet() { 92 | s := NewSet(1, 2, 3, 3, 4, 5) 93 | 94 | // Set automatically deduplicates 95 | fmt.Println(s.Size()) // 5 96 | 97 | // Add elements 98 | s.Add(6) 99 | s.Add(3) // returns false, already exists 100 | 101 | // Set operations 102 | other := NewSet(4, 5, 6, 7) 103 | union := s.Union(other) 104 | intersection := s.Intersection(other) 105 | 106 | // Functional operations 107 | filtered := s.Filter(func(x int) bool { 108 | return x > 3 109 | }) 110 | 111 | doubled := SetMap(s, func(x int) int { 112 | return x * 2 113 | }) 114 | 115 | fmt.Print(filtered) 116 | 117 | // Output: Set{4, 5, 6} 118 | } 119 | ``` 120 | 121 | ### Set Methods available: 122 | 123 | ```go 124 | func NewSet[T comparable](slice ...T) *Set[T] 125 | func (s *Set[T]) Add(item T) bool 126 | func (s *Set[T]) All(predicate func(T) bool) bool 127 | func (s *Set[T]) Any(predicate func(T) bool) bool 128 | func (s *Set[T]) Clear() 129 | func (s *Set[T]) Clone() *Set[T] 130 | func (s *Set[T]) Contains(item T) bool 131 | func (s *Set[T]) Count(predicate func(T) bool) int 132 | func (s *Set[T]) Difference(other *Set[T]) *Set[T] 133 | func (s *Set[T]) Drop(n int) *Set[T] 134 | func (s *Set[T]) Each(fn func(T)) 135 | func (s *Set[T]) Equal(other *Set[T]) bool 136 | func (s *Set[T]) Filter(predicate func(T) bool) *Set[T] 137 | func (s *Set[T]) Find(predicate func(T) bool) (T, bool) 138 | func (s *Set[T]) Intersection(other *Set[T]) *Set[T] 139 | func (s *Set[T]) IsDisjoint(other *Set[T]) bool 140 | func (s *Set[T]) IsEmpty() bool 141 | func (s *Set[T]) IsSubset(other *Set[T]) bool 142 | func (s *Set[T]) IsSuperset(other *Set[T]) bool 143 | func (s *Set[T]) None(predicate func(T) bool) bool 144 | func (s *Set[T]) Partition(predicate func(T) bool) (*Set[T], *Set[T]) 145 | func (s *Set[T]) Reject(predicate func(T) bool) *Set[T] 146 | func (s *Set[T]) Remove(item T) bool 147 | func (s *Set[T]) Size() int 148 | func (s *Set[T]) String() string 149 | func (s *Set[T]) SymmetricDifference(other *Set[T]) *Set[T] 150 | func (s *Set[T]) Take(n int) *Set[T] 151 | func (s *Set[T]) ToSlice() []T 152 | func (s *Set[T]) Union(other *Set[T]) *Set[T] 153 | func SetMap[T, U comparable](s *Set[T], fn func(T) U) *Set[U] 154 | func SetReduce[T comparable, U any](s *Set[T], initial U, fn func(U, T) U) U 155 | ``` 156 | 157 | ## Map 158 | 159 | A thread-safe generic wrapper around `sync.Map` with type-safe key-value operations 160 | 161 | ### Example 162 | 163 | ```go 164 | func ExampleMap() { 165 | m := Map[string, int]{} 166 | 167 | // Store values 168 | m.Store("one", 1) 169 | m.Store("two", 2) 170 | m.Store("three", 3) 171 | 172 | // Load values 173 | if val, ok := m.Load("two"); ok { 174 | fmt.Println(val) // 2 175 | } 176 | 177 | // Iterate over all entries 178 | m.Range(func(k string, v int) bool { 179 | fmt.Printf("%s: %d\n", k, v) 180 | return true // continue iteration 181 | }) 182 | 183 | // LoadOrStore 184 | actual, loaded := m.LoadOrStore("four", 4) 185 | fmt.Println(loaded) // false (newly stored) 186 | 187 | // Delete 188 | m.Delete("one") 189 | } 190 | ``` 191 | 192 | ### Map Methods available: 193 | 194 | ```go 195 | func (m *Map[K, V]) Delete(k K) 196 | func (m *Map[K, V]) Load(k K) (v V, ok bool) 197 | func (m *Map[K, V]) LoadAndDelete(k K) (v V, loaded bool) 198 | func (m *Map[K, V]) LoadOrStore(k K, v V) (actual V, loaded bool) 199 | func (m *Map[K, V]) Range(f func(k K, v V) bool) 200 | func (m *Map[K, V]) Store(k K, v V) 201 | func (m *Map[K, V]) Swap(k K, v V) (previous V, loaded bool) 202 | ``` 203 | -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "slices" 6 | "strings" 7 | ) 8 | 9 | // Set represents a generic set data structure that stores unique elements of type T. 10 | // T must be comparable to be used as map keys. 11 | type Set[T comparable] struct { 12 | order []T 13 | items map[T]struct{} 14 | } 15 | 16 | // NewSet creates and returns a new Set initialized with elements from the given slice. 17 | // Duplicate elements in the slice will be automatically deduplicated. 18 | func NewSet[T comparable](slice ...T) *Set[T] { 19 | s := Set[T]{ 20 | order: make([]T, 0, len(slice)), 21 | items: make(map[T]struct{}, len(slice)), 22 | } 23 | 24 | for _, item := range slice { 25 | s.Add(item) 26 | } 27 | 28 | return &s 29 | } 30 | 31 | // Add inserts an element into the set. 32 | // Returns true if the element was added (wasn't already present), false otherwise. 33 | func (s *Set[T]) Add(item T) bool { 34 | if s.Contains(item) { 35 | return false 36 | } 37 | 38 | s.items[item] = struct{}{} 39 | s.order = append(s.order, item) 40 | 41 | return true 42 | } 43 | 44 | // Remove deletes an element from the set. 45 | // Returns true if the element was removed (was present), false otherwise. 46 | func (s *Set[T]) Remove(item T) bool { 47 | if !s.Contains(item) { 48 | return false 49 | } 50 | 51 | delete(s.items, item) 52 | index := slices.Index(s.order, item) 53 | s.order = slices.Delete(s.order, index, index+1) 54 | 55 | return true 56 | } 57 | 58 | // Contains checks if an element exists in the set. 59 | // Returns true if the element is present, false otherwise. 60 | func (s *Set[T]) Contains(item T) bool { 61 | _, exists := s.items[item] 62 | return exists 63 | } 64 | 65 | // Size returns the number of elements in the set. 66 | func (s *Set[T]) Size() int { 67 | return len(s.order) 68 | } 69 | 70 | // IsEmpty returns true if the set contains no elements, false otherwise. 71 | func (s *Set[T]) IsEmpty() bool { 72 | return len(s.order) == 0 73 | } 74 | 75 | // Clear removes all elements from the set. 76 | func (s *Set[T]) Clear() { 77 | s.items = make(map[T]struct{}) 78 | s.order = []T{} 79 | } 80 | 81 | // ToSlice returns a slice containing all elements in the set in the order they were added. 82 | // the slice is not a copy, modifying it will affect the set. 83 | func (s *Set[T]) ToSlice() []T { 84 | return s.order 85 | } 86 | 87 | // Clone creates and returns a shallow copy of the set. 88 | func (s *Set[T]) Clone() *Set[T] { 89 | return NewSet(s.order...) 90 | } 91 | 92 | // Union returns a new set containing all elements that are in either this set or the other set. 93 | func (s *Set[T]) Union(other *Set[T]) *Set[T] { 94 | result := s.Clone() 95 | for _, item := range other.order { 96 | result.Add(item) 97 | } 98 | 99 | return result 100 | } 101 | 102 | // Intersection returns a new set containing only elements that are in both this 103 | // set and the other set in the order they were added to the first set. 104 | func (s *Set[T]) Intersection(other *Set[T]) *Set[T] { 105 | result := NewSet[T]() 106 | for _, item := range s.order { 107 | if other.Contains(item) { 108 | result.Add(item) 109 | } 110 | } 111 | return result 112 | } 113 | 114 | // Difference returns a new set containing elements that are in this set but not 115 | // in the other set in the order they were added to the first set. 116 | func (s *Set[T]) Difference(other *Set[T]) *Set[T] { 117 | result := NewSet[T]() 118 | for _, item := range s.order { 119 | if !other.Contains(item) { 120 | result.Add(item) 121 | } 122 | } 123 | return result 124 | } 125 | 126 | // SymmetricDifference returns a new set containing elements that are in either this set or the other set, but not in both. 127 | func (s *Set[T]) SymmetricDifference(other *Set[T]) *Set[T] { 128 | return s.Union(other).Difference(s.Intersection(other)) 129 | } 130 | 131 | // IsSubset returns true if this set is a subset of the other set (all elements of this set are in the other set). 132 | func (s *Set[T]) IsSubset(other *Set[T]) bool { 133 | for _, item := range s.order { 134 | if !other.Contains(item) { 135 | return false 136 | } 137 | } 138 | return true 139 | } 140 | 141 | // IsSuperset returns true if this set is a superset of the other set (all elements of the other set are in this set). 142 | func (s *Set[T]) IsSuperset(other *Set[T]) bool { 143 | return other.IsSubset(s) 144 | } 145 | 146 | // IsDisjoint returns true if this set has no elements in common with the other set. 147 | func (s *Set[T]) IsDisjoint(other *Set[T]) bool { 148 | return s.Intersection(other).IsEmpty() 149 | } 150 | 151 | // Equal returns true if this set contains exactly the same elements as the other set. 152 | func (s *Set[T]) Equal(other *Set[T]) bool { 153 | return s.Size() == other.Size() && s.IsSubset(other) 154 | } 155 | 156 | // Each iterates over all elements in the set in insertion order and calls the provided function for each element. 157 | func (s *Set[T]) Each(fn func(T)) { 158 | for _, item := range s.order { 159 | fn(item) 160 | } 161 | } 162 | 163 | // SetMap applies a transformation function to each element and returns a new set with the results. 164 | // The transformation function must return a comparable type. 165 | func SetMap[T, U comparable](s *Set[T], fn func(T) U) *Set[U] { 166 | result := NewSet[U]() 167 | result.order = make([]U, 0, len(s.order)) 168 | result.items = make(map[U]struct{}, len(s.order)) 169 | 170 | for _, item := range s.order { 171 | result.Add(fn(item)) 172 | } 173 | 174 | return result 175 | } 176 | 177 | // Filter returns a new set containing only elements that satisfy the predicate function. 178 | func (s *Set[T]) Filter(predicate func(T) bool) *Set[T] { 179 | result := NewSet[T]() 180 | result.order = make([]T, 0, len(s.order)) 181 | result.items = make(map[T]struct{}, len(s.order)) 182 | 183 | for _, item := range s.order { 184 | if predicate(item) { 185 | result.Add(item) 186 | } 187 | } 188 | 189 | return result 190 | } 191 | 192 | // Reject returns a new set containing only elements that do not satisfy the predicate function. 193 | // This is the opposite of Filter. 194 | func (s *Set[T]) Reject(predicate func(T) bool) *Set[T] { 195 | return s.Filter(func(item T) bool { 196 | return !predicate(item) 197 | }) 198 | } 199 | 200 | // Find returns the first element that satisfies the predicate function and true. 201 | // If no element satisfies the predicate, it returns the zero value of T and false. 202 | func (s *Set[T]) Find(predicate func(T) bool) (T, bool) { 203 | for _, item := range s.order { 204 | if predicate(item) { 205 | return item, true 206 | } 207 | } 208 | var zero T 209 | return zero, false 210 | } 211 | 212 | // All returns true if all elements in the set satisfy the predicate function. 213 | // Returns true for empty sets. 214 | func (s *Set[T]) All(predicate func(T) bool) bool { 215 | for _, item := range s.order { 216 | if !predicate(item) { 217 | return false 218 | } 219 | } 220 | return true 221 | } 222 | 223 | // Any returns true if at least one element in the set satisfies the predicate function. 224 | // Returns false for empty sets. 225 | func (s *Set[T]) Any(predicate func(T) bool) bool { 226 | return slices.ContainsFunc(s.order, predicate) 227 | } 228 | 229 | // None returns true if no elements in the set satisfy the predicate function. 230 | // Returns true for empty sets. 231 | func (s *Set[T]) None(predicate func(T) bool) bool { 232 | return !s.Any(predicate) 233 | } 234 | 235 | // Count returns the number of elements that satisfy the predicate function. 236 | func (s *Set[T]) Count(predicate func(T) bool) int { 237 | count := 0 238 | for _, item := range s.order { 239 | if predicate(item) { 240 | count++ 241 | } 242 | } 243 | return count 244 | } 245 | 246 | // SetReduce applies a reduction function to all elements in the set, starting with an initial value. 247 | // The reduction function takes the accumulated value and the current element, returning the new accumulated value. 248 | func SetReduce[T comparable, U any](s *Set[T], initial U, fn func(U, T) U) U { 249 | result := initial 250 | for _, item := range s.order { 251 | result = fn(result, item) 252 | } 253 | return result 254 | } 255 | 256 | // Partition divides the set into two new sets based on the predicate function. 257 | // Returns two sets: the first contains elements that satisfy the predicate, 258 | // the second contains elements that do not satisfy the predicate. 259 | func (s *Set[T]) Partition(predicate func(T) bool) (*Set[T], *Set[T]) { 260 | trueSet := NewSet[T]() 261 | falseSet := NewSet[T]() 262 | 263 | for _, item := range s.order { 264 | if predicate(item) { 265 | trueSet.Add(item) 266 | } else { 267 | falseSet.Add(item) 268 | } 269 | } 270 | 271 | return trueSet, falseSet 272 | } 273 | 274 | // Take returns a new set containing up to n elements from this set in the order they were added. 275 | func (s *Set[T]) Take(n int) *Set[T] { 276 | if n <= 0 { 277 | return NewSet[T]() 278 | } 279 | 280 | result := NewSet[T]() 281 | count := 0 282 | 283 | for _, item := range s.order { 284 | if count >= n { 285 | break 286 | } 287 | result.Add(item) 288 | count++ 289 | } 290 | 291 | return result 292 | } 293 | 294 | // Drop returns a new set with the first n elements removed in the order they were added. 295 | func (s *Set[T]) Drop(n int) *Set[T] { 296 | if n <= 0 { 297 | return s.Clone() 298 | } 299 | 300 | result := NewSet[T]() 301 | count := 0 302 | 303 | for _, item := range s.order { 304 | if count >= n { 305 | result.Add(item) 306 | } 307 | count++ 308 | } 309 | 310 | return result 311 | } 312 | 313 | // String returns a string representation of the set. 314 | func (s *Set[T]) String() string { 315 | if s.IsEmpty() { 316 | return "Set{}" 317 | } 318 | 319 | items := s.ToSlice() 320 | strs := make([]string, len(items)) 321 | for i, item := range items { 322 | strs[i] = fmt.Sprintf("%v", item) 323 | } 324 | 325 | return fmt.Sprintf("Set{%s}", strings.Join(strs, ", ")) 326 | } 327 | -------------------------------------------------------------------------------- /slice.go: -------------------------------------------------------------------------------- 1 | // Package types provides a generic data types similar to that of Ruby 2 | package types 3 | 4 | import ( 5 | "math/rand" 6 | "slices" 7 | ) 8 | 9 | // Slice is an alias for a slice of variables that vary in types 10 | // Slice can hold any comparable data type 11 | type Slice[T comparable] []T 12 | 13 | // At returns element by index, a negative index counts from the end of 14 | // the Slice 15 | // if index is out of range it returns nil 16 | func (a Slice[T]) At(index int) *T { 17 | len := len(a) 18 | 19 | if index < 0 { 20 | if -index <= len { 21 | return &a[len+index] 22 | } 23 | return nil 24 | } 25 | 26 | if index < len { 27 | return &a[index] 28 | } 29 | 30 | return nil 31 | } 32 | 33 | // CountElement returns number of elements equal to "element" in Slice 34 | func (a Slice[T]) CountElement(element T) (count int) { 35 | for _, o := range a { 36 | if o == element { 37 | count++ 38 | } 39 | } 40 | return count 41 | } 42 | 43 | // CountBy returns number of elements which "block" returns true for 44 | func (a Slice[T]) CountBy(block func(T) bool) (count int) { 45 | for _, o := range a { 46 | if block(o) { 47 | count++ 48 | } 49 | } 50 | return count 51 | } 52 | 53 | // Cycle will cycle through Slice elements "count" times passing each 54 | // element to "block" function 55 | func (a Slice[T]) Cycle(count int, block func(T)) { 56 | for range count { 57 | for _, v := range a { 58 | block(v) 59 | } 60 | } 61 | } 62 | 63 | // Any returns true if "block" returned true for any of the Slice elements 64 | // and false otherwise 65 | func (a Slice[T]) Any(block func(T) bool) bool { 66 | return slices.ContainsFunc(a, block) 67 | } 68 | 69 | // All returns true if "block" returned true for all elements in Slice and 70 | // false otherwise 71 | func (a Slice[T]) All(block func(T) bool) bool { 72 | for _, o := range a { 73 | if !block(o) { 74 | return false 75 | } 76 | } 77 | 78 | return true 79 | } 80 | 81 | // Delete will remove all elements that are equal to the passed element 82 | func (a Slice[T]) Delete(element T) Slice[T] { 83 | result := Slice[T]{} 84 | for _, o := range a { 85 | if o != element { 86 | result = append(result, o) 87 | } 88 | } 89 | return result 90 | } 91 | 92 | // DeleteAt will delete an element by index 93 | func (a Slice[T]) DeleteAt(index int) Slice[T] { 94 | result := make(Slice[T], 0, len(a)-1) 95 | result = append(result, a[:index]...) 96 | result = append(result, a[index+1:]...) 97 | return result 98 | } 99 | 100 | // DeleteIf will delete all elements which "block" returns true for 101 | func (a Slice[T]) DeleteIf(block func(T) bool) Slice[T] { 102 | result := Slice[T]{} 103 | for _, o := range a { 104 | if !block(o) { 105 | result = append(result, o) 106 | } 107 | } 108 | return result 109 | } 110 | 111 | // Drop will return an array without the first "count" elements from the 112 | // beginning 113 | func (a Slice[T]) Drop(count int) Slice[T] { 114 | return a[count:] 115 | } 116 | 117 | // Each will execute "block" for each element in array 118 | func (a Slice[T]) Each(block func(T)) { 119 | for _, o := range a { 120 | block(o) 121 | } 122 | } 123 | 124 | // EachIndex will execute "block" for each element index in array 125 | func (a Slice[T]) EachIndex(block func(int)) { 126 | for i := range a { 127 | block(i) 128 | } 129 | } 130 | 131 | // IsEmpty will return true of array empty, false otherwise 132 | func (a Slice[T]) IsEmpty() bool { 133 | return len(a) == 0 134 | } 135 | 136 | // IsEq returns true if array the "other" array 137 | func (a Slice[T]) IsEq(other Slice[T]) bool { 138 | // check length 139 | if len(a) != len(other) { 140 | return false 141 | } 142 | 143 | // check values 144 | for i, o := range a { 145 | if o != other[i] { 146 | return false 147 | } 148 | } 149 | 150 | return true 151 | } 152 | 153 | // Len returns number of elements in array 154 | func (a Slice[T]) Len() int { 155 | return len(a) 156 | } 157 | 158 | // Fetch will return the element in "index", if it doesn't exist it 159 | // returns the passed "defaultValue" 160 | func (a Slice[T]) Fetch(index int, defaultValue T) T { 161 | val := a.At(index) 162 | if val != nil { 163 | return *val 164 | } 165 | 166 | return defaultValue 167 | } 168 | 169 | // Fill will replace elements inplace starting from "start" counting 170 | // "length" elements with the passed "element" parameter, will return same array 171 | // object 172 | func (a Slice[T]) Fill(element T, start int, length int) Slice[T] { 173 | for length--; length >= 0; length-- { 174 | a[start+length] = element 175 | } 176 | return a 177 | } 178 | 179 | // FillWith will replace elements from start counting "length" items, 180 | // passing every index to block and replacing the element inplace with the 181 | // return value 182 | func (a Slice[T]) FillWith(start int, length int, block func(int) T) Slice[T] { 183 | for length--; length >= 0; length-- { 184 | a[start+length] = block(start + length) 185 | } 186 | return a 187 | } 188 | 189 | // Index returns the index of the first element in array that is equal to 190 | // "element", returns -1 if the elements if not found 191 | func (a Slice[T]) Index(element T) int { 192 | for i, o := range a { 193 | if o == element { 194 | return i 195 | } 196 | } 197 | return -1 198 | } 199 | 200 | // IndexBy returns first element that block returns true for, -1 otherwise 201 | func (a Slice[T]) IndexBy(block func(T) bool) int { 202 | for i, o := range a { 203 | if block(o) { 204 | return i 205 | } 206 | } 207 | 208 | return -1 209 | } 210 | 211 | // First returns first element of array 212 | func (a Slice[T]) First() *T { 213 | return a.At(0) 214 | } 215 | 216 | // Last returns last element of array 217 | func (a Slice[T]) Last() *T { 218 | return a.At(len(a) - 1) 219 | } 220 | 221 | // Firsts will return an array holding the first "count" elements of the 222 | // array 223 | func (a Slice[T]) Firsts(count int) Slice[T] { 224 | return a[0:count] 225 | } 226 | 227 | // Lasts will return an array holding the lasts "count" elements of the 228 | // array 229 | func (a Slice[T]) Lasts(count int) Slice[T] { 230 | return a[len(a)-count:] 231 | } 232 | 233 | // Include will return true if element found in the array 234 | func (a Slice[T]) Include(element T) bool { 235 | return a.Index(element) != -1 236 | } 237 | 238 | // Insert will insert a set of elements in the index and will return a new 239 | // array 240 | func (a Slice[T]) Insert(index int, elements ...T) Slice[T] { 241 | result := Slice[T]{} 242 | result = append(result, a[0:index]...) 243 | result = append(result, elements...) 244 | result = append(result, a[index:]...) 245 | return result 246 | } 247 | 248 | // KeepIf will return an array contains all elements where "block" 249 | // returned true for them 250 | func (a Slice[T]) KeepIf(block func(T) bool) Slice[T] { 251 | result := Slice[T]{} 252 | for _, o := range a { 253 | if block(o) { 254 | result = append(result, o) 255 | } 256 | } 257 | return result 258 | } 259 | 260 | // Select is an alias for KeepIf 261 | func (a Slice[T]) Select(block func(T) bool) Slice[T] { 262 | return a.KeepIf(block) 263 | } 264 | 265 | // SelectUntil selects from the start of the slice until the block returns true, 266 | // excluding the item that returned true. 267 | func (a Slice[T]) SelectUntil(block func(T) bool) Slice[T] { 268 | index := a.IndexBy(block) 269 | if index == -1 { 270 | return a 271 | } 272 | 273 | return a[0:index] 274 | } 275 | 276 | // Reduce is an alias for KeepIf 277 | func (a Slice[T]) Reduce(block func(T) bool) Slice[T] { 278 | return a.KeepIf(block) 279 | } 280 | 281 | // Map will return a new array replacing every element from current array 282 | // with the return value of the block 283 | func (a Slice[T]) Map(block func(T) T) Slice[T] { 284 | result := Slice[T]{} 285 | for _, o := range a { 286 | result = append(result, block(o)) 287 | } 288 | return result 289 | } 290 | 291 | // Max returns the element the returned the highest value when passed to 292 | // block 293 | func (a Slice[T]) Max(block func(T) int) T { 294 | if len(a) == 0 { 295 | return *new(T) 296 | } 297 | var maxElement T = a[0] 298 | var maxScore = block(a[0]) 299 | for _, o := range a[1:] { 300 | score := block(o) 301 | if score > maxScore { 302 | maxElement = o 303 | maxScore = score 304 | } 305 | } 306 | 307 | return maxElement 308 | } 309 | 310 | // Min returns the element the returned the lowest value when passed to 311 | // block 312 | func (a Slice[T]) Min(block func(T) int) T { 313 | if len(a) == 0 { 314 | return *new(T) 315 | } 316 | var minElement T = a[0] 317 | var minScore = block(a[0]) 318 | for _, o := range a[1:] { 319 | score := block(o) 320 | if score < minScore { 321 | minElement = o 322 | minScore = score 323 | } 324 | } 325 | 326 | return minElement 327 | } 328 | 329 | // Push appends an element to the array returning a new modified array 330 | func (a Slice[T]) Push(element T) Slice[T] { 331 | return append(a, element) 332 | } 333 | 334 | // Pop removes the last element from the array, returning new array and 335 | // the element 336 | func (a Slice[T]) Pop() (Slice[T], T) { 337 | return a[:len(a)-1], a[len(a)-1] 338 | } 339 | 340 | // Unshift adds an element to the array returning a new modified array 341 | func (a Slice[T]) Unshift(element T) Slice[T] { 342 | return append(Slice[T]{element}, a...) 343 | } 344 | 345 | // Shift will remove the first element of the array returning the element 346 | // and a modified array 347 | func (a Slice[T]) Shift() (T, Slice[T]) { 348 | if len(a) == 0 { 349 | return *new(T), a 350 | } 351 | return a[0], a[1:] 352 | } 353 | 354 | // Reverse will reverse the array in reverse returning the array reference 355 | // again 356 | func (a Slice[T]) Reverse() Slice[T] { 357 | for i := len(a)/2 - 1; i >= 0; i-- { 358 | opp := len(a) - 1 - i 359 | a[i], a[opp] = a[opp], a[i] 360 | } 361 | return a 362 | } 363 | 364 | // Shuffle will randomly shuffle an array elements order returning array 365 | // reference again 366 | func (a Slice[T]) Shuffle() Slice[T] { 367 | for i := len(a) - 1; i > 0; i-- { 368 | j := rand.Intn(i + 1) 369 | a[i], a[j] = a[j], a[i] 370 | } 371 | return a 372 | } 373 | 374 | // Unique returns a unique list of elements in the slice. order or the result is 375 | // same order of the items in the source slice 376 | func (a Slice[T]) Unique() Slice[T] { 377 | keys := map[T]struct{}{} 378 | out := Slice[T]{} 379 | for _, v := range a { 380 | if _, ok := keys[v]; !ok { 381 | keys[v] = struct{}{} 382 | out = append(out, v) 383 | } 384 | } 385 | 386 | return out 387 | } 388 | 389 | // Partition divides the slice into two new slices based on the predicate function. 390 | // Returns two slices: the first contains elements that satisfy the predicate, 391 | // the second contains elements that do not satisfy the predicate. 392 | func (s Slice[T]) Partition(predicate func(T) bool) (Slice[T], Slice[T]) { 393 | trueSet := Slice[T]{} 394 | falseSet := Slice[T]{} 395 | 396 | for _, item := range s { 397 | if predicate(item) { 398 | trueSet = append(trueSet, item) 399 | } else { 400 | falseSet = append(falseSet, item) 401 | } 402 | } 403 | 404 | return trueSet, falseSet 405 | } 406 | 407 | // SliceReduce applies a function against an accumulator and each element in the slice 408 | func SliceReduce[T comparable, U any](s Slice[T], initial U, fn func(U, T) U) U { 409 | result := initial 410 | for _, item := range s { 411 | result = fn(result, item) 412 | } 413 | 414 | return result 415 | } 416 | -------------------------------------------------------------------------------- /slice_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func AssertSlicesEquals[T comparable](t *testing.T, a1 Slice[T], a2 Slice[T]) { 10 | if len(a1) != len(a2) { 11 | t.Errorf("Slices doesn't have the same length %d, %d", len(a1), len(a2)) 12 | return 13 | } 14 | 15 | for i := range a1 { 16 | if a1[i] != a2[i] { 17 | t.Errorf("Expected (%v) = %v but got %v", i, a1[i], a2[i]) 18 | } 19 | } 20 | } 21 | 22 | func TestSliceAt(t *testing.T) { 23 | a := Slice[int]{1, 2, 3, 4, 5, 6, 7} 24 | tcs := map[int]int{ 25 | 1: 2, 26 | 6: 7, 27 | -1: 7, 28 | -7: 1, 29 | } 30 | 31 | for i, v := range tcs { 32 | result := a.At(i) 33 | if *result != v { 34 | t.Errorf("With %d expected %d but found %d", i, v, result) 35 | } 36 | } 37 | 38 | result := a.At(8) 39 | if result != nil { 40 | t.Errorf("With %d expected %v but found %d", 8, nil, result) 41 | } 42 | } 43 | 44 | func TestSliceLen(t *testing.T) { 45 | a := Slice[int]{1, 2, 3, 4} 46 | result := a.Len() 47 | if result != 4 { 48 | t.Errorf("Expected %d but found %d", 4, result) 49 | } 50 | } 51 | 52 | func TestSliceCountElement(t *testing.T) { 53 | a := Slice[int]{1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3} 54 | tcs := map[int]int{ 55 | 1: 3, 56 | 2: 4, 57 | 3: 5, 58 | 4: 0, 59 | } 60 | 61 | for i, v := range tcs { 62 | result := a.CountElement(i) 63 | if result != v { 64 | t.Errorf("With %d expected %d but found %d", i, v, result) 65 | } 66 | } 67 | } 68 | 69 | func TestSliceCountBy(t *testing.T) { 70 | a := Slice[int]{1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3} 71 | tcs := []struct { 72 | name string 73 | f func(e int) bool 74 | result int 75 | }{ 76 | { 77 | name: "e == 1", 78 | f: func(e int) bool { return e == 1 }, 79 | result: 3, 80 | }, 81 | { 82 | name: "e < 1", 83 | f: func(e int) bool { return e < 1 }, 84 | result: 0, 85 | }, 86 | { 87 | name: "e > 1", 88 | f: func(e int) bool { return e > 1 }, 89 | result: 9, 90 | }, 91 | } 92 | 93 | for _, tc := range tcs { 94 | result := a.CountBy(tc.f) 95 | if result != tc.result { 96 | t.Errorf("With %s expected %d but found %d", tc.name, tc.result, result) 97 | } 98 | } 99 | } 100 | 101 | func TestSliceCycle(t *testing.T) { 102 | a := Slice[int]{1, 2} 103 | 104 | elements := []int{1, 2, 1, 2, 1, 2} 105 | a.Cycle(3, func(e int) { 106 | result := e 107 | if result != elements[0] { 108 | t.Errorf("Expected %d but found %d", elements[0], result) 109 | } 110 | 111 | elements = elements[1:] 112 | }) 113 | } 114 | 115 | func TestSliceAny(t *testing.T) { 116 | a := Slice[bool]{false, false, false} 117 | identity := func(e bool) bool { 118 | return e 119 | } 120 | if a.Any(identity) { 121 | t.Error("Expected false but got true") 122 | } 123 | 124 | a = Slice[bool]{false, true, false} 125 | if !a.Any(identity) { 126 | t.Error("Expected true but got false") 127 | } 128 | } 129 | 130 | func TestSliceAll(t *testing.T) { 131 | a := Slice[bool]{true, true, true} 132 | identity := func(e bool) bool { 133 | return e 134 | } 135 | if !a.All(identity) { 136 | t.Error("Expected true but got false") 137 | } 138 | 139 | a = Slice[bool]{false, true, false} 140 | if a.All(identity) { 141 | t.Error("Expected false but got true") 142 | } 143 | } 144 | 145 | func TestSliceDelete(t *testing.T) { 146 | a := Slice[int]{1, 2, 3, 4, 1, 2, 3, 4} 147 | a = a.Delete(1) 148 | result := Slice[int]{2, 3, 4, 2, 3, 4} 149 | AssertSlicesEquals(t, result, a) 150 | } 151 | 152 | func TestSliceDeleteAt(t *testing.T) { 153 | a := Slice[int]{1, 2, 3, 4} 154 | a = a.DeleteAt(1) 155 | result := Slice[int]{1, 3, 4} 156 | AssertSlicesEquals(t, result, a) 157 | } 158 | 159 | func TestSliceDeleteAtNoMutation(t *testing.T) { 160 | // Test that DeleteAt doesn't mutate the original slice 161 | original := Slice[int]{1, 2, 3, 4, 5} 162 | originalCopy := Slice[int]{1, 2, 3, 4, 5} 163 | result := original.DeleteAt(2) 164 | 165 | // Verify the result is correct 166 | expected := Slice[int]{1, 2, 4, 5} 167 | AssertSlicesEquals(t, expected, result) 168 | 169 | // Verify original slice is unchanged 170 | AssertSlicesEquals(t, originalCopy, original) 171 | } 172 | 173 | func TestSliceDeleteIf(t *testing.T) { 174 | a := Slice[int]{1, 2, 3, 4, 5, 6} 175 | a = a.DeleteIf(func(e int) bool { 176 | return e > 1 177 | }) 178 | result := Slice[int]{1} 179 | AssertSlicesEquals(t, result, a) 180 | } 181 | 182 | func TestSliceDrop(t *testing.T) { 183 | a := Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9} 184 | a = a.Drop(5) 185 | result := Slice[int]{6, 7, 8, 9} 186 | AssertSlicesEquals(t, result, a) 187 | } 188 | 189 | func TestSliceEach(t *testing.T) { 190 | a := Slice[int]{1, 2, 3} 191 | sum := 0 192 | summer := func(e int) { sum += e } 193 | a.Each(summer) 194 | 195 | if sum != 6 { 196 | t.Errorf("Expected sum to be 6 but found %d", sum) 197 | } 198 | } 199 | 200 | func TestSliceEachIndex(t *testing.T) { 201 | a := Slice[int]{1, 2, 3} 202 | var sum int 203 | summer := func(i int) { sum += i } 204 | a.EachIndex(summer) 205 | 206 | if sum != 3 { 207 | t.Errorf("Expected sum to be 3 but found %d", sum) 208 | } 209 | } 210 | 211 | func TestSliceIsEmpty(t *testing.T) { 212 | a := Slice[int]{} 213 | if !a.IsEmpty() { 214 | t.Error("Expected to be empty but found not empty") 215 | } 216 | 217 | a = Slice[int]{1, 2, 3} 218 | if a.IsEmpty() { 219 | t.Error("Expected to be not empty but found empty") 220 | } 221 | } 222 | 223 | func TestSliceIsEq(t *testing.T) { 224 | a := Slice[int]{1, 2, 3, 4} 225 | b := Slice[int]{1, 2, 3, 4} 226 | 227 | if !a.IsEq(b) { 228 | t.Error("Expected arrat a to equal b but found otherwise") 229 | } 230 | } 231 | 232 | func TestSliceFetch(t *testing.T) { 233 | a := Slice[int]{1, 2} 234 | 235 | result := a.Fetch(0, -1) 236 | if result != 1 { 237 | t.Errorf("Expected 1 but got %d", result) 238 | } 239 | 240 | result = a.Fetch(-1, -1) 241 | if result != 2 { 242 | t.Errorf("Expected 2 but bot %d", result) 243 | } 244 | 245 | result = a.Fetch(3, -1) 246 | if result != -1 { 247 | t.Errorf("Expecte default value but got %d", result) 248 | } 249 | } 250 | 251 | func TestSliceFill(t *testing.T) { 252 | a := Slice[int]{1, 2, 3, 4, 5, 6} 253 | result := Slice[int]{1, 2, 1, 1, 1, 6} 254 | a.Fill(1, 2, 3) 255 | AssertSlicesEquals(t, result, a) 256 | } 257 | 258 | func TestSliceFillWith(t *testing.T) { 259 | a := Slice[int]{1, 2, 3, 4, 5, 6} 260 | result := Slice[int]{1, 2, 200, 300, 400, 6} 261 | a.FillWith(2, 3, func(i int) int { 262 | return i * 100 263 | }) 264 | AssertSlicesEquals(t, result, a) 265 | } 266 | 267 | func TestSliceIndex(t *testing.T) { 268 | a := Slice[int]{1, 2, 3, 4, 5, 6} 269 | if a.Index(1) != 0 { 270 | t.Errorf("Expected 1 to have index of 0 but got %d", a.Index(1)) 271 | } 272 | 273 | if a.Index(7) != -1 { 274 | t.Errorf("Expected 7 to have index of -1 gut git %d", a.Index(7)) 275 | } 276 | } 277 | 278 | func TestSliceIndexBy(t *testing.T) { 279 | a := Slice[int]{1, 2, 3, 4, 5, 6} 280 | index := a.IndexBy(func(element int) bool { 281 | return element > 2 282 | }) 283 | if index != 2 { 284 | t.Errorf("Expected element 3 index to be 2 got %d instead", index) 285 | } 286 | 287 | index = a.IndexBy(func(element int) bool { 288 | return element == -1 289 | }) 290 | if index != -1 { 291 | t.Errorf("Expected element -1 index to be -1 go %d instead", index) 292 | } 293 | } 294 | 295 | func TestSliceFirst(t *testing.T) { 296 | a := Slice[int]{1, 2, 3, 4} 297 | if *a.First() != 1 { 298 | t.Errorf("Expected first element to be 1 got %d", a.First()) 299 | } 300 | 301 | a = Slice[int]{} 302 | if a.First() != nil { 303 | t.Errorf("Expected first element to be nil got %d", a.First()) 304 | } 305 | } 306 | 307 | func TestSliceLast(t *testing.T) { 308 | a := Slice[int]{1, 2, 3, 4} 309 | if *a.Last() != 4 { 310 | t.Errorf("Expected last element to be 4 got %d", a.Last()) 311 | } 312 | 313 | a = Slice[int]{} 314 | if a.Last() != nil { 315 | t.Errorf("Expected last element to be nil got %d", a.Last()) 316 | } 317 | } 318 | 319 | func TestSliceFirsts(t *testing.T) { 320 | a := Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9} 321 | result := Slice[int]{1, 2, 3} 322 | AssertSlicesEquals(t, result, a.Firsts(3)) 323 | } 324 | 325 | func TestSliceLasts(t *testing.T) { 326 | a := Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9} 327 | result := Slice[int]{7, 8, 9} 328 | AssertSlicesEquals(t, result, a.Lasts(3)) 329 | } 330 | 331 | func TestSliceInclude(t *testing.T) { 332 | a := Slice[int]{1, 2, 3, 4} 333 | if !a.Include(1) { 334 | t.Error("Expected 1 to be found but didn't find it") 335 | } 336 | 337 | if a.Include(-1) { 338 | t.Error("Expected the string not to be found but it was found!") 339 | } 340 | } 341 | 342 | func TestSliceInsert(t *testing.T) { 343 | a := Slice[int]{1, 2, 3, 4} 344 | result := Slice[int]{1, 2, 0, 3, 4} 345 | b := a.Insert(2, 0) 346 | AssertSlicesEquals(t, result, b) 347 | 348 | result = Slice[int]{1, 2, 3, 4, 0} 349 | c := a.Insert(4, 0) 350 | AssertSlicesEquals(t, result, c) 351 | 352 | result = Slice[int]{0, 1, 2, 3, 4} 353 | d := a.Insert(0, 0) 354 | AssertSlicesEquals(t, result, d) 355 | } 356 | 357 | func TestSliceKeepIf(t *testing.T) { 358 | a := Slice[int]{1, 2, 3, 4, 5, 6} 359 | a = a.KeepIf(func(e int) bool { 360 | return e > 3 361 | }) 362 | result := Slice[int]{4, 5, 6} 363 | AssertSlicesEquals(t, result, a) 364 | } 365 | 366 | func TestSliceSelect(t *testing.T) { 367 | a := Slice[int]{1, 2, 3, 4, 5, 6} 368 | a = a.Select(func(e int) bool { 369 | return e > 3 370 | }) 371 | result := Slice[int]{4, 5, 6} 372 | AssertSlicesEquals(t, result, a) 373 | } 374 | 375 | func TestSliceSelectUntil(t *testing.T) { 376 | a := Slice[int]{1, 2, 3, 4, 5, 6} 377 | a = a.SelectUntil(func(e int) bool { 378 | return e == 3 379 | }) 380 | result := Slice[int]{1, 2} 381 | AssertSlicesEquals(t, result, a) 382 | } 383 | 384 | func TestSliceMap(t *testing.T) { 385 | a := Slice[int]{1, 2, 3, 4, 5} 386 | inc := func(e int) int { 387 | return e + 100 388 | } 389 | result := Slice[int]{101, 102, 103, 104, 105} 390 | AssertSlicesEquals(t, result, a.Map(inc)) 391 | } 392 | 393 | func TestSliceMax(t *testing.T) { 394 | a := Slice[int]{1, 2, 3, 4} 395 | identity := func(e int) int { 396 | return e 397 | } 398 | 399 | result := a.Max(identity) 400 | if result != 4 { 401 | t.Errorf("Expected max to be 4 found %d", result) 402 | } 403 | 404 | a = Slice[int]{} 405 | result = a.Max(identity) 406 | if result != 0 { 407 | t.Errorf("Expected max of empty array to be nil got %d", result) 408 | } 409 | } 410 | 411 | func TestSliceMin(t *testing.T) { 412 | a := Slice[int]{4, 3, 2, 1} 413 | identity := func(e int) int { 414 | return e 415 | } 416 | 417 | result := a.Min(identity) 418 | if result != 1 { 419 | t.Errorf("Expected min to be 4 found %d", result) 420 | } 421 | 422 | a = Slice[int]{} 423 | result = a.Min(identity) 424 | if result != 0 { 425 | t.Errorf("Expected min of empty array to be %d", result) 426 | } 427 | } 428 | 429 | func TestSlicePush(t *testing.T) { 430 | a := Slice[int]{1, 2} 431 | a = a.Push(3) 432 | result := Slice[int]{1, 2, 3} 433 | AssertSlicesEquals(t, result, a) 434 | } 435 | 436 | func TestSlicePop(t *testing.T) { 437 | a := Slice[int]{1, 2, 3} 438 | a, e := a.Pop() 439 | result := Slice[int]{1, 2} 440 | if e != 3 { 441 | t.Errorf("Expected element to be 3 got %d", e) 442 | } 443 | AssertSlicesEquals(t, result, a) 444 | } 445 | 446 | func TestSliceUnshift(t *testing.T) { 447 | a := Slice[int]{1, 2, 3} 448 | a = a.Unshift(4) 449 | result := Slice[int]{4, 1, 2, 3} 450 | AssertSlicesEquals(t, result, a) 451 | } 452 | 453 | func TestSliceShift(t *testing.T) { 454 | a := Slice[int]{1, 2, 3} 455 | e, a := a.Shift() 456 | result := Slice[int]{2, 3} 457 | AssertSlicesEquals(t, result, a) 458 | if e != 1 { 459 | t.Errorf("Expected element to be 1 got %d", e) 460 | } 461 | 462 | a = Slice[int]{} 463 | e, a = a.Shift() 464 | if e != 0 { 465 | t.Errorf("Expected element to be nil got %d", e) 466 | } 467 | } 468 | 469 | func TestSliceReverse(t *testing.T) { 470 | a := Slice[int]{1, 2, 3} 471 | a = a.Reverse() 472 | result := Slice[int]{3, 2, 1} 473 | AssertSlicesEquals(t, result, a) 474 | } 475 | 476 | func TestSliceShuffle(t *testing.T) { 477 | a := Slice[int]{1, 2, 3, 4} 478 | a = a.Shuffle() 479 | notResult := Slice[int]{1, 2, 3, 4} 480 | if a.IsEq(notResult) { 481 | t.Error("Expected arrays not to equal after shuffle but it was the same") 482 | } 483 | } 484 | 485 | func TestSliceUnique(t *testing.T) { 486 | a := Slice[int]{1, 2, 1, 3, 1, 2, 3, 4} 487 | a = a.Unique() 488 | result := Slice[int]{1, 2, 3, 4} 489 | AssertSlicesEquals(t, result, a) 490 | } 491 | 492 | func TestSliceReduce(t *testing.T) { 493 | tests := []struct { 494 | name string 495 | initial []int 496 | initial_val int 497 | reduceFn func(int, int) int 498 | want int 499 | }{ 500 | { 501 | name: "empty slice", 502 | initial: []int{}, 503 | initial_val: 0, 504 | reduceFn: func(acc, n int) int { return acc + n }, 505 | want: 0, 506 | }, 507 | { 508 | name: "sum all elements", 509 | initial: []int{1, 2, 3, 4, 5}, 510 | initial_val: 0, 511 | reduceFn: func(acc, n int) int { return acc + n }, 512 | want: 15, 513 | }, 514 | { 515 | name: "multiply all elements", 516 | initial: []int{2, 3, 4}, 517 | initial_val: 1, 518 | reduceFn: func(acc, n int) int { return acc * n }, 519 | want: 24, 520 | }, 521 | { 522 | name: "find maximum", 523 | initial: []int{3, 7, 2, 9, 1}, 524 | initial_val: 0, 525 | reduceFn: func(acc, n int) int { 526 | if n > acc { 527 | return n 528 | } 529 | return acc 530 | }, 531 | want: 9, 532 | }, 533 | { 534 | name: "concatenate as string lengths", 535 | initial: []int{10, 100, 1000}, 536 | initial_val: 0, 537 | reduceFn: func(acc, n int) int { return acc + len(fmt.Sprintf("%d", n)) }, 538 | want: 9, // 2 + 3 + 4 characters 539 | }, 540 | } 541 | 542 | for _, tt := range tests { 543 | t.Run(tt.name, func(t *testing.T) { 544 | s := Slice[int](tt.initial) 545 | got := SliceReduce(s, tt.initial_val, tt.reduceFn) 546 | 547 | if got != tt.want { 548 | t.Errorf("Reduce() = %v, want %v", got, tt.want) 549 | } 550 | }) 551 | } 552 | } 553 | 554 | func TestSlice_Partition(t *testing.T) { 555 | tests := []struct { 556 | name string 557 | initial []int 558 | predicate func(int) bool 559 | wantTrue []int 560 | wantFalse []int 561 | }{ 562 | { 563 | name: "empty slice", 564 | initial: []int{}, 565 | predicate: func(n int) bool { return n%2 == 0 }, 566 | wantTrue: []int{}, 567 | wantFalse: []int{}, 568 | }, 569 | { 570 | name: "partition even and odd", 571 | initial: []int{1, 2, 3, 4, 5, 6}, 572 | predicate: func(n int) bool { return n%2 == 0 }, 573 | wantTrue: []int{2, 4, 6}, 574 | wantFalse: []int{1, 3, 5}, 575 | }, 576 | { 577 | name: "partition greater than 3", 578 | initial: []int{1, 2, 3, 4, 5}, 579 | predicate: func(n int) bool { return n > 3 }, 580 | wantTrue: []int{4, 5}, 581 | wantFalse: []int{1, 2, 3}, 582 | }, 583 | { 584 | name: "all elements satisfy predicate", 585 | initial: []int{2, 4, 6}, 586 | predicate: func(n int) bool { return n%2 == 0 }, 587 | wantTrue: []int{2, 4, 6}, 588 | wantFalse: []int{}, 589 | }, 590 | { 591 | name: "no elements satisfy predicate", 592 | initial: []int{1, 3, 5}, 593 | predicate: func(n int) bool { return n%2 == 0 }, 594 | wantTrue: []int{}, 595 | wantFalse: []int{1, 3, 5}, 596 | }, 597 | } 598 | 599 | for _, tt := range tests { 600 | t.Run(tt.name, func(t *testing.T) { 601 | s := Slice[int](tt.initial) 602 | gotTrue, gotFalse := s.Partition(tt.predicate) 603 | 604 | if !reflect.DeepEqual([]int(gotTrue), []int(tt.wantTrue)) { 605 | t.Errorf("Partition() true set = %v, want %v", gotTrue, tt.wantTrue) 606 | } 607 | 608 | if !reflect.DeepEqual([]int(gotFalse), []int(tt.wantFalse)) { 609 | t.Errorf("Partition() false set = %v, want %v", gotFalse, tt.wantFalse) 610 | } 611 | }) 612 | } 613 | } 614 | 615 | func BenchmarkSlicePartition(b *testing.B) { 616 | // Create a large slice to test performance 617 | size := 1000 618 | s := make(Slice[int], size) 619 | for i := 0; i < size; i++ { 620 | s[i] = i 621 | } 622 | 623 | b.ResetTimer() 624 | for i := 0; i < b.N; i++ { 625 | s.Partition(func(n int) bool { return n%2 == 0 }) 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /set_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sort" 7 | "testing" 8 | ) 9 | 10 | func TestNewSet(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | slice []int 14 | want []int 15 | }{ 16 | { 17 | name: "empty slice", 18 | slice: []int{}, 19 | want: []int{}, 20 | }, 21 | { 22 | name: "single element", 23 | slice: []int{1}, 24 | want: []int{1}, 25 | }, 26 | { 27 | name: "multiple unique elements", 28 | slice: []int{1, 2, 3}, 29 | want: []int{1, 2, 3}, 30 | }, 31 | { 32 | name: "duplicate elements", 33 | slice: []int{1, 2, 2, 3, 1}, 34 | want: []int{1, 2, 3}, 35 | }, 36 | } 37 | 38 | for _, tt := range tests { 39 | t.Run(tt.name, func(t *testing.T) { 40 | set := NewSet(tt.slice...) 41 | got := set.ToSlice() 42 | sort.Ints(got) 43 | sort.Ints(tt.want) 44 | 45 | if !reflect.DeepEqual(got, tt.want) { 46 | t.Errorf("New() = %v, want %v", got, tt.want) 47 | } 48 | }) 49 | } 50 | } 51 | 52 | func TestSet_Add(t *testing.T) { 53 | tests := []struct { 54 | name string 55 | initial []int 56 | add int 57 | want bool 58 | wantSize int 59 | }{ 60 | { 61 | name: "add to empty set", 62 | initial: []int{}, 63 | add: 1, 64 | want: true, 65 | wantSize: 1, 66 | }, 67 | { 68 | name: "add unique element", 69 | initial: []int{1, 2}, 70 | add: 3, 71 | want: true, 72 | wantSize: 3, 73 | }, 74 | { 75 | name: "add duplicate element", 76 | initial: []int{1, 2, 3}, 77 | add: 2, 78 | want: false, 79 | wantSize: 3, 80 | }, 81 | } 82 | 83 | for _, tt := range tests { 84 | t.Run(tt.name, func(t *testing.T) { 85 | set := NewSet(tt.initial...) 86 | got := set.Add(tt.add) 87 | 88 | if got != tt.want { 89 | t.Errorf("Add() = %v, want %v", got, tt.want) 90 | } 91 | if set.Size() != tt.wantSize { 92 | t.Errorf("Add() size = %v, want %v", set.Size(), tt.wantSize) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func TestSet_Remove(t *testing.T) { 99 | tests := []struct { 100 | name string 101 | initial []int 102 | remove int 103 | want bool 104 | wantSize int 105 | }{ 106 | { 107 | name: "remove from empty set", 108 | initial: []int{}, 109 | remove: 1, 110 | want: false, 111 | wantSize: 0, 112 | }, 113 | { 114 | name: "remove existing element", 115 | initial: []int{1, 2, 3}, 116 | remove: 2, 117 | want: true, 118 | wantSize: 2, 119 | }, 120 | { 121 | name: "remove non-existing element", 122 | initial: []int{1, 2, 3}, 123 | remove: 4, 124 | want: false, 125 | wantSize: 3, 126 | }, 127 | } 128 | 129 | for _, tt := range tests { 130 | t.Run(tt.name, func(t *testing.T) { 131 | set := NewSet(tt.initial...) 132 | got := set.Remove(tt.remove) 133 | 134 | if got != tt.want { 135 | t.Errorf("Remove() = %v, want %v", got, tt.want) 136 | } 137 | if set.Size() != tt.wantSize { 138 | t.Errorf("Remove() size = %v, want %v", set.Size(), tt.wantSize) 139 | } 140 | }) 141 | } 142 | } 143 | 144 | func TestSet_Contains(t *testing.T) { 145 | tests := []struct { 146 | name string 147 | initial []int 148 | check int 149 | want bool 150 | }{ 151 | { 152 | name: "empty set", 153 | initial: []int{}, 154 | check: 1, 155 | want: false, 156 | }, 157 | { 158 | name: "element exists", 159 | initial: []int{1, 2, 3}, 160 | check: 2, 161 | want: true, 162 | }, 163 | { 164 | name: "element does not exist", 165 | initial: []int{1, 2, 3}, 166 | check: 4, 167 | want: false, 168 | }, 169 | } 170 | 171 | for _, tt := range tests { 172 | t.Run(tt.name, func(t *testing.T) { 173 | set := NewSet(tt.initial...) 174 | got := set.Contains(tt.check) 175 | 176 | if got != tt.want { 177 | t.Errorf("Contains() = %v, want %v", got, tt.want) 178 | } 179 | }) 180 | } 181 | } 182 | 183 | func TestSet_Size(t *testing.T) { 184 | tests := []struct { 185 | name string 186 | initial []int 187 | want int 188 | }{ 189 | { 190 | name: "empty set", 191 | initial: []int{}, 192 | want: 0, 193 | }, 194 | { 195 | name: "single element", 196 | initial: []int{1}, 197 | want: 1, 198 | }, 199 | { 200 | name: "multiple elements", 201 | initial: []int{1, 2, 3, 4, 5}, 202 | want: 5, 203 | }, 204 | { 205 | name: "duplicates removed", 206 | initial: []int{1, 1, 2, 2, 3}, 207 | want: 3, 208 | }, 209 | } 210 | 211 | for _, tt := range tests { 212 | t.Run(tt.name, func(t *testing.T) { 213 | set := NewSet(tt.initial...) 214 | got := set.Size() 215 | 216 | if got != tt.want { 217 | t.Errorf("Size() = %v, want %v", got, tt.want) 218 | } 219 | }) 220 | } 221 | } 222 | 223 | func TestSet_IsEmpty(t *testing.T) { 224 | tests := []struct { 225 | name string 226 | initial []int 227 | want bool 228 | }{ 229 | { 230 | name: "empty set", 231 | initial: []int{}, 232 | want: true, 233 | }, 234 | { 235 | name: "non-empty set", 236 | initial: []int{1, 2, 3}, 237 | want: false, 238 | }, 239 | } 240 | 241 | for _, tt := range tests { 242 | t.Run(tt.name, func(t *testing.T) { 243 | set := NewSet(tt.initial...) 244 | got := set.IsEmpty() 245 | 246 | if got != tt.want { 247 | t.Errorf("IsEmpty() = %v, want %v", got, tt.want) 248 | } 249 | }) 250 | } 251 | } 252 | 253 | func TestSet_Clear(t *testing.T) { 254 | tests := []struct { 255 | name string 256 | initial []int 257 | }{ 258 | { 259 | name: "clear empty set", 260 | initial: []int{}, 261 | }, 262 | { 263 | name: "clear non-empty set", 264 | initial: []int{1, 2, 3, 4, 5}, 265 | }, 266 | } 267 | 268 | for _, tt := range tests { 269 | t.Run(tt.name, func(t *testing.T) { 270 | set := NewSet(tt.initial...) 271 | set.Clear() 272 | 273 | if !set.IsEmpty() { 274 | t.Errorf("Clear() should result in empty set") 275 | } 276 | if set.Size() != 0 { 277 | t.Errorf("Clear() size = %v, want 0", set.Size()) 278 | } 279 | }) 280 | } 281 | } 282 | 283 | func TestSet_ToSlice(t *testing.T) { 284 | tests := []struct { 285 | name string 286 | initial []int 287 | want []int 288 | }{ 289 | { 290 | name: "empty set", 291 | initial: []int{}, 292 | want: []int{}, 293 | }, 294 | { 295 | name: "single element", 296 | initial: []int{1}, 297 | want: []int{1}, 298 | }, 299 | { 300 | name: "multiple elements", 301 | initial: []int{3, 1, 2}, 302 | want: []int{1, 2, 3}, 303 | }, 304 | } 305 | 306 | for _, tt := range tests { 307 | t.Run(tt.name, func(t *testing.T) { 308 | set := NewSet(tt.initial...) 309 | got := set.ToSlice() 310 | sort.Ints(got) 311 | 312 | if !reflect.DeepEqual(got, tt.want) { 313 | t.Errorf("ToSlice() = %v, want %v", got, tt.want) 314 | } 315 | }) 316 | } 317 | } 318 | 319 | func TestSet_Clone(t *testing.T) { 320 | tests := []struct { 321 | name string 322 | initial []int 323 | }{ 324 | { 325 | name: "clone empty set", 326 | initial: []int{}, 327 | }, 328 | { 329 | name: "clone non-empty set", 330 | initial: []int{1, 2, 3, 4, 5}, 331 | }, 332 | } 333 | 334 | for _, tt := range tests { 335 | t.Run(tt.name, func(t *testing.T) { 336 | original := NewSet(tt.initial...) 337 | clone := original.Clone() 338 | 339 | if !original.Equal(clone) { 340 | t.Errorf("Clone() should be equal to original") 341 | } 342 | 343 | // Test independence 344 | clone.Add(999) 345 | if original.Contains(999) { 346 | t.Errorf("Clone() should be independent of original") 347 | } 348 | }) 349 | } 350 | } 351 | 352 | func TestSet_Union(t *testing.T) { 353 | tests := []struct { 354 | name string 355 | set1 []int 356 | set2 []int 357 | want []int 358 | }{ 359 | { 360 | name: "union with empty sets", 361 | set1: []int{}, 362 | set2: []int{}, 363 | want: []int{}, 364 | }, 365 | { 366 | name: "union with one empty set", 367 | set1: []int{1, 2, 3}, 368 | set2: []int{}, 369 | want: []int{1, 2, 3}, 370 | }, 371 | { 372 | name: "union with no overlap", 373 | set1: []int{1, 2}, 374 | set2: []int{3, 4}, 375 | want: []int{1, 2, 3, 4}, 376 | }, 377 | { 378 | name: "union with overlap", 379 | set1: []int{1, 2, 3}, 380 | set2: []int{2, 3, 4}, 381 | want: []int{1, 2, 3, 4}, 382 | }, 383 | { 384 | name: "union with identical sets", 385 | set1: []int{1, 2, 3}, 386 | set2: []int{1, 2, 3}, 387 | want: []int{1, 2, 3}, 388 | }, 389 | } 390 | 391 | for _, tt := range tests { 392 | t.Run(tt.name, func(t *testing.T) { 393 | s1 := NewSet(tt.set1...) 394 | s2 := NewSet(tt.set2...) 395 | result := s1.Union(s2) 396 | got := result.ToSlice() 397 | sort.Ints(got) 398 | 399 | if !reflect.DeepEqual(got, tt.want) { 400 | t.Errorf("Union() = %v, want %v", got, tt.want) 401 | } 402 | }) 403 | } 404 | } 405 | 406 | func TestSet_Intersection(t *testing.T) { 407 | tests := []struct { 408 | name string 409 | set1 []int 410 | set2 []int 411 | want []int 412 | }{ 413 | { 414 | name: "intersection with empty sets", 415 | set1: []int{}, 416 | set2: []int{}, 417 | want: []int{}, 418 | }, 419 | { 420 | name: "intersection with one empty set", 421 | set1: []int{1, 2, 3}, 422 | set2: []int{}, 423 | want: []int{}, 424 | }, 425 | { 426 | name: "intersection with no overlap", 427 | set1: []int{1, 2}, 428 | set2: []int{3, 4}, 429 | want: []int{}, 430 | }, 431 | { 432 | name: "intersection with overlap", 433 | set1: []int{1, 2, 3}, 434 | set2: []int{2, 3, 4}, 435 | want: []int{2, 3}, 436 | }, 437 | { 438 | name: "intersection with identical sets", 439 | set1: []int{1, 2, 3}, 440 | set2: []int{1, 2, 3}, 441 | want: []int{1, 2, 3}, 442 | }, 443 | } 444 | 445 | for _, tt := range tests { 446 | t.Run(tt.name, func(t *testing.T) { 447 | s1 := NewSet(tt.set1...) 448 | s2 := NewSet(tt.set2...) 449 | result := s1.Intersection(s2) 450 | got := result.ToSlice() 451 | sort.Ints(got) 452 | 453 | if !reflect.DeepEqual(got, tt.want) { 454 | t.Errorf("Intersection() = %v, want %v", got, tt.want) 455 | } 456 | }) 457 | } 458 | } 459 | 460 | func TestSet_Difference(t *testing.T) { 461 | tests := []struct { 462 | name string 463 | set1 []int 464 | set2 []int 465 | want []int 466 | }{ 467 | { 468 | name: "difference with empty sets", 469 | set1: []int{}, 470 | set2: []int{}, 471 | want: []int{}, 472 | }, 473 | { 474 | name: "difference with empty second set", 475 | set1: []int{1, 2, 3}, 476 | set2: []int{}, 477 | want: []int{1, 2, 3}, 478 | }, 479 | { 480 | name: "difference with empty first set", 481 | set1: []int{}, 482 | set2: []int{1, 2, 3}, 483 | want: []int{}, 484 | }, 485 | { 486 | name: "difference with no overlap", 487 | set1: []int{1, 2}, 488 | set2: []int{3, 4}, 489 | want: []int{1, 2}, 490 | }, 491 | { 492 | name: "difference with overlap", 493 | set1: []int{1, 2, 3}, 494 | set2: []int{2, 3, 4}, 495 | want: []int{1}, 496 | }, 497 | { 498 | name: "difference with identical sets", 499 | set1: []int{1, 2, 3}, 500 | set2: []int{1, 2, 3}, 501 | want: []int{}, 502 | }, 503 | } 504 | 505 | for _, tt := range tests { 506 | t.Run(tt.name, func(t *testing.T) { 507 | s1 := NewSet(tt.set1...) 508 | s2 := NewSet(tt.set2...) 509 | result := s1.Difference(s2) 510 | got := result.ToSlice() 511 | sort.Ints(got) 512 | 513 | if !reflect.DeepEqual(got, tt.want) { 514 | t.Errorf("Difference() = %v, want %v", got, tt.want) 515 | } 516 | }) 517 | } 518 | } 519 | 520 | func TestSet_SymmetricDifference(t *testing.T) { 521 | tests := []struct { 522 | name string 523 | set1 []int 524 | set2 []int 525 | want []int 526 | }{ 527 | { 528 | name: "symmetric difference with empty sets", 529 | set1: []int{}, 530 | set2: []int{}, 531 | want: []int{}, 532 | }, 533 | { 534 | name: "symmetric difference with one empty set", 535 | set1: []int{1, 2, 3}, 536 | set2: []int{}, 537 | want: []int{1, 2, 3}, 538 | }, 539 | { 540 | name: "symmetric difference with no overlap", 541 | set1: []int{1, 2}, 542 | set2: []int{3, 4}, 543 | want: []int{1, 2, 3, 4}, 544 | }, 545 | { 546 | name: "symmetric difference with overlap", 547 | set1: []int{1, 2, 3}, 548 | set2: []int{2, 3, 4}, 549 | want: []int{1, 4}, 550 | }, 551 | { 552 | name: "symmetric difference with identical sets", 553 | set1: []int{1, 2, 3}, 554 | set2: []int{1, 2, 3}, 555 | want: []int{}, 556 | }, 557 | } 558 | 559 | for _, tt := range tests { 560 | t.Run(tt.name, func(t *testing.T) { 561 | s1 := NewSet(tt.set1...) 562 | s2 := NewSet(tt.set2...) 563 | result := s1.SymmetricDifference(s2) 564 | got := result.ToSlice() 565 | sort.Ints(got) 566 | 567 | if !reflect.DeepEqual(got, tt.want) { 568 | t.Errorf("SymmetricDifference() = %v, want %v", got, tt.want) 569 | } 570 | }) 571 | } 572 | } 573 | 574 | func TestSet_IsSubset(t *testing.T) { 575 | tests := []struct { 576 | name string 577 | set1 []int 578 | set2 []int 579 | want bool 580 | }{ 581 | { 582 | name: "empty set is subset of empty set", 583 | set1: []int{}, 584 | set2: []int{}, 585 | want: true, 586 | }, 587 | { 588 | name: "empty set is subset of non-empty set", 589 | set1: []int{}, 590 | set2: []int{1, 2, 3}, 591 | want: true, 592 | }, 593 | { 594 | name: "non-empty set is not subset of empty set", 595 | set1: []int{1, 2}, 596 | set2: []int{}, 597 | want: false, 598 | }, 599 | { 600 | name: "proper subset", 601 | set1: []int{1, 2}, 602 | set2: []int{1, 2, 3}, 603 | want: true, 604 | }, 605 | { 606 | name: "not a subset", 607 | set1: []int{1, 2, 4}, 608 | set2: []int{1, 2, 3}, 609 | want: false, 610 | }, 611 | { 612 | name: "identical sets are subsets", 613 | set1: []int{1, 2, 3}, 614 | set2: []int{1, 2, 3}, 615 | want: true, 616 | }, 617 | } 618 | 619 | for _, tt := range tests { 620 | t.Run(tt.name, func(t *testing.T) { 621 | s1 := NewSet(tt.set1...) 622 | s2 := NewSet(tt.set2...) 623 | got := s1.IsSubset(s2) 624 | 625 | if got != tt.want { 626 | t.Errorf("IsSubset() = %v, want %v", got, tt.want) 627 | } 628 | }) 629 | } 630 | } 631 | 632 | func TestSet_IsSuperset(t *testing.T) { 633 | tests := []struct { 634 | name string 635 | set1 []int 636 | set2 []int 637 | want bool 638 | }{ 639 | { 640 | name: "empty set is superset of empty set", 641 | set1: []int{}, 642 | set2: []int{}, 643 | want: true, 644 | }, 645 | { 646 | name: "non-empty set is superset of empty set", 647 | set1: []int{1, 2, 3}, 648 | set2: []int{}, 649 | want: true, 650 | }, 651 | { 652 | name: "empty set is not superset of non-empty set", 653 | set1: []int{}, 654 | set2: []int{1, 2}, 655 | want: false, 656 | }, 657 | { 658 | name: "proper superset", 659 | set1: []int{1, 2, 3}, 660 | set2: []int{1, 2}, 661 | want: true, 662 | }, 663 | { 664 | name: "not a superset", 665 | set1: []int{1, 2, 3}, 666 | set2: []int{1, 2, 4}, 667 | want: false, 668 | }, 669 | { 670 | name: "identical sets are supersets", 671 | set1: []int{1, 2, 3}, 672 | set2: []int{1, 2, 3}, 673 | want: true, 674 | }, 675 | } 676 | 677 | for _, tt := range tests { 678 | t.Run(tt.name, func(t *testing.T) { 679 | s1 := NewSet(tt.set1...) 680 | s2 := NewSet(tt.set2...) 681 | got := s1.IsSuperset(s2) 682 | 683 | if got != tt.want { 684 | t.Errorf("IsSuperset() = %v, want %v", got, tt.want) 685 | } 686 | }) 687 | } 688 | } 689 | 690 | func TestSet_IsDisjoint(t *testing.T) { 691 | tests := []struct { 692 | name string 693 | set1 []int 694 | set2 []int 695 | want bool 696 | }{ 697 | { 698 | name: "empty sets are disjoint", 699 | set1: []int{}, 700 | set2: []int{}, 701 | want: true, 702 | }, 703 | { 704 | name: "empty and non-empty sets are disjoint", 705 | set1: []int{}, 706 | set2: []int{1, 2, 3}, 707 | want: true, 708 | }, 709 | { 710 | name: "disjoint sets", 711 | set1: []int{1, 2}, 712 | set2: []int{3, 4}, 713 | want: true, 714 | }, 715 | { 716 | name: "overlapping sets are not disjoint", 717 | set1: []int{1, 2, 3}, 718 | set2: []int{2, 3, 4}, 719 | want: false, 720 | }, 721 | { 722 | name: "identical sets are not disjoint", 723 | set1: []int{1, 2, 3}, 724 | set2: []int{1, 2, 3}, 725 | want: false, 726 | }, 727 | } 728 | 729 | for _, tt := range tests { 730 | t.Run(tt.name, func(t *testing.T) { 731 | s1 := NewSet(tt.set1...) 732 | s2 := NewSet(tt.set2...) 733 | got := s1.IsDisjoint(s2) 734 | 735 | if got != tt.want { 736 | t.Errorf("IsDisjoint() = %v, want %v", got, tt.want) 737 | } 738 | }) 739 | } 740 | } 741 | 742 | func TestSet_Equal(t *testing.T) { 743 | tests := []struct { 744 | name string 745 | set1 []int 746 | set2 []int 747 | want bool 748 | }{ 749 | { 750 | name: "empty sets are equal", 751 | set1: []int{}, 752 | set2: []int{}, 753 | want: true, 754 | }, 755 | { 756 | name: "empty and non-empty sets are not equal", 757 | set1: []int{}, 758 | set2: []int{1, 2, 3}, 759 | want: false, 760 | }, 761 | { 762 | name: "identical sets are equal", 763 | set1: []int{1, 2, 3}, 764 | set2: []int{1, 2, 3}, 765 | want: true, 766 | }, 767 | { 768 | name: "different order same elements are equal", 769 | set1: []int{3, 1, 2}, 770 | set2: []int{1, 2, 3}, 771 | want: true, 772 | }, 773 | { 774 | name: "different sets are not equal", 775 | set1: []int{1, 2, 3}, 776 | set2: []int{1, 2, 4}, 777 | want: false, 778 | }, 779 | { 780 | name: "different size sets are not equal", 781 | set1: []int{1, 2}, 782 | set2: []int{1, 2, 3}, 783 | want: false, 784 | }, 785 | } 786 | 787 | for _, tt := range tests { 788 | t.Run(tt.name, func(t *testing.T) { 789 | s1 := NewSet(tt.set1...) 790 | s2 := NewSet(tt.set2...) 791 | got := s1.Equal(s2) 792 | 793 | if got != tt.want { 794 | t.Errorf("Equal() = %v, want %v", got, tt.want) 795 | } 796 | }) 797 | } 798 | } 799 | 800 | func TestSet_Each(t *testing.T) { 801 | tests := []struct { 802 | name string 803 | initial []int 804 | want map[int]bool // track which elements were visited 805 | }{ 806 | { 807 | name: "empty set", 808 | initial: []int{}, 809 | want: map[int]bool{}, 810 | }, 811 | { 812 | name: "single element", 813 | initial: []int{1}, 814 | want: map[int]bool{1: true}, 815 | }, 816 | { 817 | name: "multiple elements", 818 | initial: []int{1, 2, 3}, 819 | want: map[int]bool{1: true, 2: true, 3: true}, 820 | }, 821 | } 822 | 823 | for _, tt := range tests { 824 | t.Run(tt.name, func(t *testing.T) { 825 | set := NewSet(tt.initial...) 826 | visited := make(map[int]bool) 827 | 828 | set.Each(func(item int) { 829 | visited[item] = true 830 | }) 831 | 832 | if !reflect.DeepEqual(visited, tt.want) { 833 | t.Errorf("Each() visited = %v, want %v", visited, tt.want) 834 | } 835 | }) 836 | } 837 | } 838 | 839 | func TestSet_EachOrderPreserved(t *testing.T) { 840 | // Test that Each iterates in insertion order 841 | set := NewSet(5, 2, 8, 1, 9, 3) 842 | 843 | var result []int 844 | set.Each(func(item int) { 845 | result = append(result, item) 846 | }) 847 | 848 | // Verify elements are in insertion order 849 | expected := []int{5, 2, 8, 1, 9, 3} 850 | if !reflect.DeepEqual(result, expected) { 851 | t.Errorf("Each() did not preserve insertion order. Got %v, want %v", result, expected) 852 | } 853 | } 854 | 855 | func TestSetMap(t *testing.T) { 856 | tests := []struct { 857 | name string 858 | initial []int 859 | mapFn func(int) string 860 | want []string 861 | }{ 862 | { 863 | name: "empty set", 864 | initial: []int{}, 865 | mapFn: func(n int) string { return fmt.Sprintf("%d", n) }, 866 | want: []string{}, 867 | }, 868 | { 869 | name: "int to string", 870 | initial: []int{1, 2, 3}, 871 | mapFn: func(n int) string { return fmt.Sprintf("num_%d", n) }, 872 | want: []string{"num_1", "num_2", "num_3"}, 873 | }, 874 | } 875 | 876 | for _, tt := range tests { 877 | t.Run(tt.name, func(t *testing.T) { 878 | set := NewSet(tt.initial...) 879 | 880 | if tt.name == "square numbers" { 881 | // Special case for int to int mapping 882 | mapFn := func(n int) int { return n * n } 883 | result := SetMap(set, mapFn) 884 | got := result.ToSlice() 885 | sort.Ints(got) 886 | want := tt.want 887 | sort.Strings(want) 888 | 889 | if !reflect.DeepEqual(got, want) { 890 | t.Errorf("Map() = %v, want %v", got, want) 891 | } 892 | } else { 893 | // For string mapping tests 894 | result := SetMap(set, tt.mapFn) 895 | got := result.ToSlice() 896 | sort.Strings(got) 897 | want := tt.want 898 | sort.Strings(want) 899 | 900 | if !reflect.DeepEqual(got, want) { 901 | t.Errorf("Map() = %v, want %v", got, want) 902 | } 903 | } 904 | }) 905 | } 906 | } 907 | 908 | func TestSet_Filter(t *testing.T) { 909 | tests := []struct { 910 | name string 911 | initial []int 912 | predicate func(int) bool 913 | want []int 914 | }{ 915 | { 916 | name: "empty set", 917 | initial: []int{}, 918 | predicate: func(n int) bool { return n > 0 }, 919 | want: []int{}, 920 | }, 921 | { 922 | name: "filter even numbers", 923 | initial: []int{1, 2, 3, 4, 5, 6}, 924 | predicate: func(n int) bool { return n%2 == 0 }, 925 | want: []int{2, 4, 6}, 926 | }, 927 | { 928 | name: "filter greater than 3", 929 | initial: []int{1, 2, 3, 4, 5}, 930 | predicate: func(n int) bool { return n > 3 }, 931 | want: []int{4, 5}, 932 | }, 933 | { 934 | name: "filter all elements (none match)", 935 | initial: []int{1, 2, 3}, 936 | predicate: func(n int) bool { return n > 10 }, 937 | want: []int{}, 938 | }, 939 | { 940 | name: "filter no elements (all match)", 941 | initial: []int{1, 2, 3}, 942 | predicate: func(n int) bool { return n > 0 }, 943 | want: []int{1, 2, 3}, 944 | }, 945 | } 946 | 947 | for _, tt := range tests { 948 | t.Run(tt.name, func(t *testing.T) { 949 | set := NewSet(tt.initial...) 950 | result := set.Filter(tt.predicate) 951 | got := result.ToSlice() 952 | sort.Ints(got) 953 | 954 | if !reflect.DeepEqual(got, tt.want) { 955 | t.Errorf("Filter() = %v, want %v", got, tt.want) 956 | } 957 | }) 958 | } 959 | } 960 | 961 | func TestSet_Reject(t *testing.T) { 962 | tests := []struct { 963 | name string 964 | initial []int 965 | predicate func(int) bool 966 | want []int 967 | }{ 968 | { 969 | name: "empty set", 970 | initial: []int{}, 971 | predicate: func(n int) bool { return n > 0 }, 972 | want: []int{}, 973 | }, 974 | { 975 | name: "reject even numbers", 976 | initial: []int{1, 2, 3, 4, 5, 6}, 977 | predicate: func(n int) bool { return n%2 == 0 }, 978 | want: []int{1, 3, 5}, 979 | }, 980 | { 981 | name: "reject greater than 3", 982 | initial: []int{1, 2, 3, 4, 5}, 983 | predicate: func(n int) bool { return n > 3 }, 984 | want: []int{1, 2, 3}, 985 | }, 986 | { 987 | name: "reject all elements (all match predicate)", 988 | initial: []int{1, 2, 3}, 989 | predicate: func(n int) bool { return n > 0 }, 990 | want: []int{}, 991 | }, 992 | { 993 | name: "reject no elements (none match predicate)", 994 | initial: []int{1, 2, 3}, 995 | predicate: func(n int) bool { return n > 10 }, 996 | want: []int{1, 2, 3}, 997 | }, 998 | } 999 | 1000 | for _, tt := range tests { 1001 | t.Run(tt.name, func(t *testing.T) { 1002 | set := NewSet(tt.initial...) 1003 | result := set.Reject(tt.predicate) 1004 | got := result.ToSlice() 1005 | sort.Ints(got) 1006 | 1007 | if !reflect.DeepEqual(got, tt.want) { 1008 | t.Errorf("Reject() = %v, want %v", got, tt.want) 1009 | } 1010 | }) 1011 | } 1012 | } 1013 | 1014 | func TestSet_Find(t *testing.T) { 1015 | tests := []struct { 1016 | name string 1017 | initial []int 1018 | predicate func(int) bool 1019 | wantValue int 1020 | wantFound bool 1021 | }{ 1022 | { 1023 | name: "empty set", 1024 | initial: []int{}, 1025 | predicate: func(n int) bool { return n > 0 }, 1026 | wantValue: 0, 1027 | wantFound: false, 1028 | }, 1029 | { 1030 | name: "find first even number", 1031 | initial: []int{1, 3, 5, 2, 4, 6}, 1032 | predicate: func(n int) bool { return n%2 == 0 }, 1033 | wantValue: 0, // Can be any even number since order is not guaranteed 1034 | wantFound: true, 1035 | }, 1036 | { 1037 | name: "find element greater than 10", 1038 | initial: []int{1, 2, 3, 15, 20}, 1039 | predicate: func(n int) bool { return n > 10 }, 1040 | wantValue: 0, // Can be 15 or 20 1041 | wantFound: true, 1042 | }, 1043 | { 1044 | name: "no element matches", 1045 | initial: []int{1, 2, 3}, 1046 | predicate: func(n int) bool { return n > 10 }, 1047 | wantValue: 0, 1048 | wantFound: false, 1049 | }, 1050 | } 1051 | 1052 | for _, tt := range tests { 1053 | t.Run(tt.name, func(t *testing.T) { 1054 | set := NewSet(tt.initial...) 1055 | gotValue, gotFound := set.Find(tt.predicate) 1056 | 1057 | if gotFound != tt.wantFound { 1058 | t.Errorf("Find() found = %v, want %v", gotFound, tt.wantFound) 1059 | } 1060 | 1061 | if tt.wantFound && gotFound { 1062 | // Verify the found value satisfies the predicate 1063 | if !tt.predicate(gotValue) { 1064 | t.Errorf("Find() returned value %v that doesn't satisfy predicate", gotValue) 1065 | } 1066 | // Verify the found value was in the original set 1067 | if !set.Contains(gotValue) { 1068 | t.Errorf("Find() returned value %v that wasn't in the set", gotValue) 1069 | } 1070 | } 1071 | }) 1072 | } 1073 | } 1074 | 1075 | func TestSet_All(t *testing.T) { 1076 | tests := []struct { 1077 | name string 1078 | initial []int 1079 | predicate func(int) bool 1080 | want bool 1081 | }{ 1082 | { 1083 | name: "empty set - should return true", 1084 | initial: []int{}, 1085 | predicate: func(n int) bool { return n > 0 }, 1086 | want: true, 1087 | }, 1088 | { 1089 | name: "all elements are positive", 1090 | initial: []int{1, 2, 3, 4, 5}, 1091 | predicate: func(n int) bool { return n > 0 }, 1092 | want: true, 1093 | }, 1094 | { 1095 | name: "not all elements are even", 1096 | initial: []int{2, 3, 4, 6}, 1097 | predicate: func(n int) bool { return n%2 == 0 }, 1098 | want: false, 1099 | }, 1100 | { 1101 | name: "all elements are greater than 10", 1102 | initial: []int{11, 12, 13}, 1103 | predicate: func(n int) bool { return n > 10 }, 1104 | want: true, 1105 | }, 1106 | { 1107 | name: "not all elements are greater than 5", 1108 | initial: []int{1, 6, 7, 8}, 1109 | predicate: func(n int) bool { return n > 5 }, 1110 | want: false, 1111 | }, 1112 | } 1113 | 1114 | for _, tt := range tests { 1115 | t.Run(tt.name, func(t *testing.T) { 1116 | set := NewSet(tt.initial...) 1117 | got := set.All(tt.predicate) 1118 | 1119 | if got != tt.want { 1120 | t.Errorf("All() = %v, want %v", got, tt.want) 1121 | } 1122 | }) 1123 | } 1124 | } 1125 | 1126 | func TestSet_Any(t *testing.T) { 1127 | tests := []struct { 1128 | name string 1129 | initial []int 1130 | predicate func(int) bool 1131 | want bool 1132 | }{ 1133 | { 1134 | name: "empty set - should return false", 1135 | initial: []int{}, 1136 | predicate: func(n int) bool { return n > 0 }, 1137 | want: false, 1138 | }, 1139 | { 1140 | name: "at least one element is even", 1141 | initial: []int{1, 3, 4, 5}, 1142 | predicate: func(n int) bool { return n%2 == 0 }, 1143 | want: true, 1144 | }, 1145 | { 1146 | name: "no element is greater than 10", 1147 | initial: []int{1, 2, 3, 4, 5}, 1148 | predicate: func(n int) bool { return n > 10 }, 1149 | want: false, 1150 | }, 1151 | { 1152 | name: "at least one element is negative", 1153 | initial: []int{-1, 2, 3}, 1154 | predicate: func(n int) bool { return n < 0 }, 1155 | want: true, 1156 | }, 1157 | { 1158 | name: "all elements satisfy predicate", 1159 | initial: []int{2, 4, 6, 8}, 1160 | predicate: func(n int) bool { return n%2 == 0 }, 1161 | want: true, 1162 | }, 1163 | } 1164 | 1165 | for _, tt := range tests { 1166 | t.Run(tt.name, func(t *testing.T) { 1167 | set := NewSet(tt.initial...) 1168 | got := set.Any(tt.predicate) 1169 | 1170 | if got != tt.want { 1171 | t.Errorf("Any() = %v, want %v", got, tt.want) 1172 | } 1173 | }) 1174 | } 1175 | } 1176 | 1177 | func TestSet_None(t *testing.T) { 1178 | tests := []struct { 1179 | name string 1180 | initial []int 1181 | predicate func(int) bool 1182 | want bool 1183 | }{ 1184 | { 1185 | name: "empty set - should return true", 1186 | initial: []int{}, 1187 | predicate: func(n int) bool { return n > 0 }, 1188 | want: true, 1189 | }, 1190 | { 1191 | name: "no element is even", 1192 | initial: []int{1, 3, 5, 7}, 1193 | predicate: func(n int) bool { return n%2 == 0 }, 1194 | want: true, 1195 | }, 1196 | { 1197 | name: "some elements are greater than 5", 1198 | initial: []int{1, 6, 7, 8}, 1199 | predicate: func(n int) bool { return n > 5 }, 1200 | want: false, 1201 | }, 1202 | { 1203 | name: "no element is negative", 1204 | initial: []int{1, 2, 3}, 1205 | predicate: func(n int) bool { return n < 0 }, 1206 | want: true, 1207 | }, 1208 | { 1209 | name: "some elements are positive", 1210 | initial: []int{1, 2, 3}, 1211 | predicate: func(n int) bool { return n > 0 }, 1212 | want: false, 1213 | }, 1214 | } 1215 | 1216 | for _, tt := range tests { 1217 | t.Run(tt.name, func(t *testing.T) { 1218 | set := NewSet(tt.initial...) 1219 | got := set.None(tt.predicate) 1220 | 1221 | if got != tt.want { 1222 | t.Errorf("None() = %v, want %v", got, tt.want) 1223 | } 1224 | }) 1225 | } 1226 | } 1227 | 1228 | func TestSet_Count(t *testing.T) { 1229 | tests := []struct { 1230 | name string 1231 | initial []int 1232 | predicate func(int) bool 1233 | want int 1234 | }{ 1235 | { 1236 | name: "empty set", 1237 | initial: []int{}, 1238 | predicate: func(n int) bool { return n > 0 }, 1239 | want: 0, 1240 | }, 1241 | { 1242 | name: "count even numbers", 1243 | initial: []int{1, 2, 3, 4, 5, 6}, 1244 | predicate: func(n int) bool { return n%2 == 0 }, 1245 | want: 3, 1246 | }, 1247 | { 1248 | name: "count numbers greater than 3", 1249 | initial: []int{1, 2, 3, 4, 5}, 1250 | predicate: func(n int) bool { return n > 3 }, 1251 | want: 2, 1252 | }, 1253 | { 1254 | name: "no elements match", 1255 | initial: []int{1, 2, 3}, 1256 | predicate: func(n int) bool { return n > 10 }, 1257 | want: 0, 1258 | }, 1259 | { 1260 | name: "all elements match", 1261 | initial: []int{1, 2, 3}, 1262 | predicate: func(n int) bool { return n > 0 }, 1263 | want: 3, 1264 | }, 1265 | } 1266 | 1267 | for _, tt := range tests { 1268 | t.Run(tt.name, func(t *testing.T) { 1269 | set := NewSet(tt.initial...) 1270 | got := set.Count(tt.predicate) 1271 | 1272 | if got != tt.want { 1273 | t.Errorf("Count() = %v, want %v", got, tt.want) 1274 | } 1275 | }) 1276 | } 1277 | } 1278 | 1279 | func TestSetReduce(t *testing.T) { 1280 | tests := []struct { 1281 | name string 1282 | initial []int 1283 | initial_val int 1284 | reduceFn func(int, int) int 1285 | want int 1286 | }{ 1287 | { 1288 | name: "empty set", 1289 | initial: []int{}, 1290 | initial_val: 0, 1291 | reduceFn: func(acc, n int) int { return acc + n }, 1292 | want: 0, 1293 | }, 1294 | { 1295 | name: "sum all elements", 1296 | initial: []int{1, 2, 3, 4, 5}, 1297 | initial_val: 0, 1298 | reduceFn: func(acc, n int) int { return acc + n }, 1299 | want: 15, 1300 | }, 1301 | { 1302 | name: "multiply all elements", 1303 | initial: []int{2, 3, 4}, 1304 | initial_val: 1, 1305 | reduceFn: func(acc, n int) int { return acc * n }, 1306 | want: 24, 1307 | }, 1308 | { 1309 | name: "find maximum", 1310 | initial: []int{3, 7, 2, 9, 1}, 1311 | initial_val: 0, 1312 | reduceFn: func(acc, n int) int { 1313 | if n > acc { 1314 | return n 1315 | } 1316 | return acc 1317 | }, 1318 | want: 9, 1319 | }, 1320 | { 1321 | name: "concatenate as string lengths", 1322 | initial: []int{10, 100, 1000}, 1323 | initial_val: 0, 1324 | reduceFn: func(acc, n int) int { return acc + len(fmt.Sprintf("%d", n)) }, 1325 | want: 9, // 2 + 3 + 4 characters 1326 | }, 1327 | } 1328 | 1329 | for _, tt := range tests { 1330 | t.Run(tt.name, func(t *testing.T) { 1331 | set := NewSet(tt.initial...) 1332 | got := SetReduce(set, tt.initial_val, tt.reduceFn) 1333 | 1334 | if got != tt.want { 1335 | t.Errorf("Reduce() = %v, want %v", got, tt.want) 1336 | } 1337 | }) 1338 | } 1339 | } 1340 | 1341 | func TestSet_Partition(t *testing.T) { 1342 | tests := []struct { 1343 | name string 1344 | initial []int 1345 | predicate func(int) bool 1346 | wantTrue []int 1347 | wantFalse []int 1348 | }{ 1349 | { 1350 | name: "empty set", 1351 | initial: []int{}, 1352 | predicate: func(n int) bool { return n%2 == 0 }, 1353 | wantTrue: []int{}, 1354 | wantFalse: []int{}, 1355 | }, 1356 | { 1357 | name: "partition even and odd", 1358 | initial: []int{1, 2, 3, 4, 5, 6}, 1359 | predicate: func(n int) bool { return n%2 == 0 }, 1360 | wantTrue: []int{2, 4, 6}, 1361 | wantFalse: []int{1, 3, 5}, 1362 | }, 1363 | { 1364 | name: "partition greater than 3", 1365 | initial: []int{1, 2, 3, 4, 5}, 1366 | predicate: func(n int) bool { return n > 3 }, 1367 | wantTrue: []int{4, 5}, 1368 | wantFalse: []int{1, 2, 3}, 1369 | }, 1370 | { 1371 | name: "all elements satisfy predicate", 1372 | initial: []int{2, 4, 6}, 1373 | predicate: func(n int) bool { return n%2 == 0 }, 1374 | wantTrue: []int{2, 4, 6}, 1375 | wantFalse: []int{}, 1376 | }, 1377 | { 1378 | name: "no elements satisfy predicate", 1379 | initial: []int{1, 3, 5}, 1380 | predicate: func(n int) bool { return n%2 == 0 }, 1381 | wantTrue: []int{}, 1382 | wantFalse: []int{1, 3, 5}, 1383 | }, 1384 | } 1385 | 1386 | for _, tt := range tests { 1387 | t.Run(tt.name, func(t *testing.T) { 1388 | set := NewSet(tt.initial...) 1389 | gotTrue, gotFalse := set.Partition(tt.predicate) 1390 | 1391 | gotTrueSlice := gotTrue.ToSlice() 1392 | gotFalseSlice := gotFalse.ToSlice() 1393 | sort.Ints(gotTrueSlice) 1394 | sort.Ints(gotFalseSlice) 1395 | 1396 | if !reflect.DeepEqual(gotTrueSlice, tt.wantTrue) { 1397 | t.Errorf("Partition() true set = %v, want %v", gotTrueSlice, tt.wantTrue) 1398 | } 1399 | if !reflect.DeepEqual(gotFalseSlice, tt.wantFalse) { 1400 | t.Errorf("Partition() false set = %v, want %v", gotFalseSlice, tt.wantFalse) 1401 | } 1402 | }) 1403 | } 1404 | } 1405 | 1406 | func TestSet_Take(t *testing.T) { 1407 | tests := []struct { 1408 | name string 1409 | initial []int 1410 | n int 1411 | wantSize int 1412 | }{ 1413 | { 1414 | name: "take from empty set", 1415 | initial: []int{}, 1416 | n: 3, 1417 | wantSize: 0, 1418 | }, 1419 | { 1420 | name: "take zero elements", 1421 | initial: []int{1, 2, 3, 4, 5}, 1422 | n: 0, 1423 | wantSize: 0, 1424 | }, 1425 | { 1426 | name: "take negative elements", 1427 | initial: []int{1, 2, 3, 4, 5}, 1428 | n: -1, 1429 | wantSize: 0, 1430 | }, 1431 | { 1432 | name: "take fewer than available", 1433 | initial: []int{1, 2, 3, 4, 5}, 1434 | n: 3, 1435 | wantSize: 3, 1436 | }, 1437 | { 1438 | name: "take more than available", 1439 | initial: []int{1, 2, 3}, 1440 | n: 5, 1441 | wantSize: 3, 1442 | }, 1443 | { 1444 | name: "take exact amount", 1445 | initial: []int{1, 2, 3}, 1446 | n: 3, 1447 | wantSize: 3, 1448 | }, 1449 | } 1450 | 1451 | for _, tt := range tests { 1452 | t.Run(tt.name, func(t *testing.T) { 1453 | set := NewSet(tt.initial...) 1454 | result := set.Take(tt.n) 1455 | 1456 | if result.Size() != tt.wantSize { 1457 | t.Errorf("Take() size = %v, want %v", result.Size(), tt.wantSize) 1458 | } 1459 | 1460 | // Verify all elements in result are from original set 1461 | result.Each(func(item int) { 1462 | if !set.Contains(item) { 1463 | t.Errorf("Take() returned element %v not in original set", item) 1464 | } 1465 | }) 1466 | }) 1467 | } 1468 | } 1469 | 1470 | func TestSet_Drop(t *testing.T) { 1471 | tests := []struct { 1472 | name string 1473 | initial []int 1474 | n int 1475 | wantSize int 1476 | }{ 1477 | { 1478 | name: "drop from empty set", 1479 | initial: []int{}, 1480 | n: 3, 1481 | wantSize: 0, 1482 | }, 1483 | { 1484 | name: "drop zero elements", 1485 | initial: []int{1, 2, 3, 4, 5}, 1486 | n: 0, 1487 | wantSize: 5, 1488 | }, 1489 | { 1490 | name: "drop negative elements", 1491 | initial: []int{1, 2, 3, 4, 5}, 1492 | n: -1, 1493 | wantSize: 5, 1494 | }, 1495 | { 1496 | name: "drop fewer than available", 1497 | initial: []int{1, 2, 3, 4, 5}, 1498 | n: 3, 1499 | wantSize: 2, 1500 | }, 1501 | { 1502 | name: "drop more than available", 1503 | initial: []int{1, 2, 3}, 1504 | n: 5, 1505 | wantSize: 0, 1506 | }, 1507 | { 1508 | name: "drop exact amount", 1509 | initial: []int{1, 2, 3}, 1510 | n: 3, 1511 | wantSize: 0, 1512 | }, 1513 | } 1514 | 1515 | for _, tt := range tests { 1516 | t.Run(tt.name, func(t *testing.T) { 1517 | set := NewSet(tt.initial...) 1518 | result := set.Drop(tt.n) 1519 | 1520 | if result.Size() != tt.wantSize { 1521 | t.Errorf("Drop() size = %v, want %v", result.Size(), tt.wantSize) 1522 | } 1523 | 1524 | // Verify all elements in result are from original set 1525 | result.Each(func(item int) { 1526 | if !set.Contains(item) { 1527 | t.Errorf("Drop() returned element %v not in original set", item) 1528 | } 1529 | }) 1530 | }) 1531 | } 1532 | } 1533 | 1534 | func TestSet_String(t *testing.T) { 1535 | tests := []struct { 1536 | name string 1537 | initial []int 1538 | want string 1539 | }{ 1540 | { 1541 | name: "empty set", 1542 | initial: []int{}, 1543 | want: "Set{}", 1544 | }, 1545 | { 1546 | name: "multiple elements", 1547 | initial: []int{1, 2, 3}, 1548 | want: "Set{1, 2, 3}", 1549 | }, 1550 | } 1551 | 1552 | for _, tt := range tests { 1553 | t.Run(tt.name, func(t *testing.T) { 1554 | set := NewSet(tt.initial...) 1555 | got := set.String() 1556 | 1557 | if got != tt.want { 1558 | t.Errorf("String() = %v, want %v", got, tt.want) 1559 | } 1560 | }) 1561 | } 1562 | } 1563 | 1564 | // Additional integration tests 1565 | func TestSet_Integration(t *testing.T) { 1566 | t.Run("chaining operations", func(t *testing.T) { 1567 | // Create a set, add elements, perform operations 1568 | set := NewSet[int]() 1569 | set.Add(1) 1570 | set.Add(2) 1571 | set.Add(3) 1572 | set.Add(4) 1573 | set.Add(5) 1574 | 1575 | // Filter even numbers, then check size 1576 | evens := set.Filter(func(n int) bool { return n%2 == 0 }) 1577 | if evens.Size() != 2 { 1578 | t.Errorf("Expected 2 even numbers, got %d", evens.Size()) 1579 | } 1580 | 1581 | // Union with another set 1582 | other := NewSet(6, 7, 8) 1583 | combined := set.Union(other) 1584 | if combined.Size() != 8 { 1585 | t.Errorf("Expected 8 elements after union, got %d", combined.Size()) 1586 | } 1587 | 1588 | // Check intersection 1589 | intersection := evens.Intersection(other) 1590 | if !intersection.IsEmpty() { 1591 | t.Errorf("Expected empty intersection, got %v", intersection.ToSlice()) 1592 | } 1593 | }) 1594 | 1595 | t.Run("type safety with strings", func(t *testing.T) { 1596 | stringSet := NewSet("apple", "banana", "cherry") 1597 | 1598 | // Test basic operations 1599 | stringSet.Add("date") 1600 | if !stringSet.Contains("apple") { 1601 | t.Error("String set should contain 'apple'") 1602 | } 1603 | 1604 | // Test filter 1605 | longWords := stringSet.Filter(func(s string) bool { return len(s) > 5 }) 1606 | expected := []string{"banana", "cherry"} 1607 | got := longWords.ToSlice() 1608 | sort.Strings(got) 1609 | 1610 | if !reflect.DeepEqual(got, expected) { 1611 | t.Errorf("Long words filter: got %v, want %v", got, expected) 1612 | } 1613 | }) 1614 | } 1615 | --------------------------------------------------------------------------------