├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── internal └── testutils │ └── utils.go ├── list ├── comparable_list.go ├── comparable_list_test.go ├── list.go └── list_test.go ├── maps ├── maps.go └── maps_test.go ├── orderedset ├── orderedset.go └── orderedset_test.go ├── set ├── set.go └── set_test.go └── slices ├── delegated_slices.go ├── delegated_sort.go ├── slices.go └── slices_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jesse Duffield 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | This is a repo for some helper methods/structs that involve generics (added in Go 1.18). 4 | 5 | ## slices package 6 | 7 | This package contains all the functions in the official slices [package](https://pkg.go.dev/golang.org/x/exp/slices#Insert) but adds extra functions as well, resulting in a superset of the official API. Any official functions are just forwarded to the official implementations. This allows you to use this package wherever you would otherwise use the official slices package. 8 | 9 | As the official slices package evolves, so too will this package. If a function is added to the official package that does basically the same thing as a function from this package, we'll replace our function for the official function. 10 | 11 | Here are the newly added functions. 12 | 13 | ```go 14 | func Some[T any](slice []T, test func(T) bool) bool 15 | func Every[T any](slice []T, test func(T) bool) bool 16 | func ForEach[T any](slice []T, f func(T)) 17 | func ForEachWithIndex[T any](slice []T, f func(T, int)) 18 | func TryForEach[T any](slice []T, f func(T) error) error 19 | func TryForEachWithIndex[T any](slice []T, f func(T, int) error) error 20 | func Map[T any, V any](slice []T, f func(T) V) []V 21 | func MapWithIndex[T any, V any](slice []T, f func(T, int) V) []V 22 | func TryMap[T any, V any](slice []T, f func(T) (V, error)) ([]V, error) 23 | func TryMapWithIndex[T any, V any](slice []T, f func(T, int) (V, error)) ([]V, error) 24 | func MapInPlace[T any](slice []T, f func(T) T) 25 | func Filter[T any](slice []T, test func(T) bool) []T 26 | func FilterWithIndex[T any](slice []T, f func(T, int) bool) []T 27 | func TryFilter[T any](slice []T, test func(T) (bool, error)) ([]T, error) 28 | func TryFilterWithIndex[T any](slice []T, test func(T, int) (bool, error)) ([]T, error) 29 | func FilterInPlace[T any](slice []T, test func(T) bool) []T 30 | func FilterMap[T any, E any](slice []T, test func(T) (E, bool)) []E 31 | func TryFilterMap[T any, E any](slice []T, test func(T) (E, bool, error)) ([]E, error) 32 | func TryFilterMapWithIndex[T any, E any](slice []T, test func(T, int) (E, bool, error)) 33 | func FlatMap[T any, V any](slice []T, f func(T) []V) []V 34 | func Flatten[T any](slice [][]T) []T 35 | func Find[T any](slice []T, f func(T) bool) (T, bool) 36 | func FindMap[T any, V any](slice []T, f func(T) (V, bool)) (V, bool) 37 | func Reverse[T any](slice []T) []T 38 | func ReverseInPlace[T any](slice []T) 39 | func Prepend[T any](slice []T, values ...T) []T 40 | func Remove[T any](slice []T, index int) []T 41 | func Move[T any](slice []T, fromIndex int, toIndex int) []T 42 | func Swap[T any](slice []T, index1 int, index2 int) 43 | func Concat[T any](slice []T, values ...T) []T 44 | func ContainsFunc[T any](slice []T, f func(T) bool) bool 45 | func Pop[T any](slice []T) (T, []T) 46 | func Shift[T any](slice []T) (T, []T) 47 | func Partition[T any](slice []T, test func(T) bool) ([]T, []T) 48 | func MaxBy[T any, V constraints.Ordered](slice []T, f func(T) V) V 49 | func MinBy[T any, V constraints.Ordered](slice []T, f func(T) V) V 50 | ``` 51 | 52 | There's a good chance I'll have the Map/Filter functions take an index argument unconditionally and leave it to the user to omit that if they want. That will cut down on the number of functions here, but add some boilerplate. I'm currently comparing both approaches on a sizable repo to help decide. 53 | 54 | ## list package 55 | 56 | This package provides a List struct which wraps a slice and gives you access to all the above functions, with a couple exceptions. For example, there's no Map method because go does not support type parameters on struct methods. 57 | 58 | ## set package 59 | 60 | This package provides a Set struct with the following methods: 61 | 62 | ```go 63 | Add(values ...T) 64 | AddSlice(slice []T) 65 | Remove(value T) 66 | RemoveSlice(slice []T) 67 | Includes(value T) bool 68 | Len() int 69 | ToSlice() []T 70 | ``` 71 | 72 | ## orderedset package 73 | 74 | This package provides an OrderedSet struct with the following methods: 75 | 76 | ```go 77 | Add(values ...T) 78 | AddSlice(slice []T) 79 | Remove(value T) 80 | RemoveSlice(slice []T) 81 | Includes(value T) bool 82 | Len() int 83 | ToSliceFromOldest() []T 84 | ToSliceFromNewest() []T 85 | ``` 86 | 87 | The difference to Set is that the insertion order of the values is preserved. 88 | 89 | ## maps package 90 | 91 | Provides some helper methods for maps: 92 | 93 | ```go 94 | func Keys[Key comparable, Value any](m map[Key]Value) []Key 95 | func Values[Key comparable, Value any](m map[Key]Value) []Value 96 | func TransformValues[Key comparable, Value any, NewValue any 97 | func TransformKeys[Key comparable, Value any, NewKey comparable](m map[Key]Value, fn func(Key) NewKey) map[NewKey]Value 98 | func MapToSlice[Key comparable, Value any, Mapped any](m map[Key]Value, f func(Key, Value) Mapped) []Mapped 99 | func Filter[Key comparable, Value any](m map[Key]Value, f func(Key, Value) bool) map[Key]Value 100 | ``` 101 | 102 | ## Alternatives 103 | 104 | Check out https://github.com/samber/lo for some more generic helper functions 105 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jesseduffield/generics 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/wk8/go-ordered-map/v2 v2.1.8 7 | golang.org/x/exp v0.0.0-20220317015231-48e79f11773a 8 | ) 9 | 10 | require ( 11 | github.com/bahlo/generic-list-go v0.2.0 // indirect 12 | github.com/buger/jsonparser v1.1.1 // indirect 13 | github.com/mailru/easyjson v0.7.7 // indirect 14 | gopkg.in/yaml.v3 v3.0.1 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= 2 | github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= 3 | github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= 4 | github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 7 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 8 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 10 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 11 | github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= 12 | github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 13 | golang.org/x/exp v0.0.0-20220317015231-48e79f11773a h1:DAzrdbxsb5tXNOhMCSwF7ZdfMbW46hE9fSVO6BsmUZM= 14 | golang.org/x/exp v0.0.0-20220317015231-48e79f11773a/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 18 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 19 | -------------------------------------------------------------------------------- /internal/testutils/utils.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import "testing" 4 | 5 | func ExpectSlice[T comparable](t *testing.T, expected []T, actual []T) { 6 | t.Helper() 7 | 8 | if len(expected) != len(actual) { 9 | t.Errorf("Expected slice %v, got %v", expected, actual) 10 | return 11 | } 12 | for i := range expected { 13 | if expected[i] != actual[i] { 14 | t.Errorf("Expected slice %v, got %v", expected, actual) 15 | return 16 | } 17 | } 18 | } 19 | 20 | func ExpectMap[T comparable, V comparable](t *testing.T, expected map[T]V, actual map[T]V) { 21 | t.Helper() 22 | 23 | if len(expected) != len(actual) { 24 | t.Errorf("Expected map %v, got %v", expected, actual) 25 | return 26 | } 27 | for key := range expected { 28 | if expected[key] != actual[key] { 29 | t.Errorf("Expected map %v, got %v", expected, actual) 30 | return 31 | } 32 | } 33 | } 34 | 35 | func ExpectError(t *testing.T, err error, expected string) { 36 | t.Helper() 37 | 38 | if err == nil { 39 | t.Errorf("Expected error, got nil") 40 | return 41 | } 42 | actual := err.Error() 43 | if actual != expected { 44 | t.Errorf("Error message is %s, expected %s", actual, expected) 45 | } 46 | } 47 | 48 | func ExpectNilError(t *testing.T, err error) { 49 | t.Helper() 50 | 51 | if err != nil { 52 | t.Errorf("Expected nil error, got %v", err) 53 | } 54 | } 55 | 56 | func ExpectPanic(t *testing.T) { 57 | t.Helper() 58 | 59 | if r := recover(); r == nil { 60 | t.Errorf("The code did not panic") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /list/comparable_list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "github.com/jesseduffield/generics/slices" 5 | ) 6 | 7 | type ComparableList[T comparable] struct { 8 | *List[T] 9 | } 10 | 11 | func NewComparable[T comparable]() *ComparableList[T] { 12 | return &ComparableList[T]{List: New[T]()} 13 | } 14 | 15 | func NewComparableFromSlice[T comparable](slice []T) *ComparableList[T] { 16 | return &ComparableList[T]{List: NewFromSlice(slice)} 17 | } 18 | 19 | func (l *ComparableList[T]) Equal(other *ComparableList[T]) bool { 20 | return l.EqualSlice(other.ToSlice()) 21 | } 22 | 23 | func (l *ComparableList[T]) EqualSlice(other []T) bool { 24 | return slices.Equal(l.ToSlice(), other) 25 | } 26 | 27 | func (l *ComparableList[T]) Compact() { 28 | l.slice = slices.Compact(l.slice) 29 | } 30 | 31 | func (l *ComparableList[T]) Index(needle T) int { 32 | return slices.Index(l.slice, needle) 33 | } 34 | 35 | func (l *ComparableList[T]) Contains(needle T) bool { 36 | return slices.Contains(l.slice, needle) 37 | } 38 | 39 | // Sorts in-place 40 | func (l *ComparableList[T]) SortFunc(test func(a T, b T) bool) { 41 | slices.SortFunc(l.slice, test) 42 | } 43 | -------------------------------------------------------------------------------- /list/comparable_list_test.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jesseduffield/generics/internal/testutils" 7 | ) 8 | 9 | func TestEqual(t *testing.T) { 10 | tests := []struct { 11 | first []int 12 | second []int 13 | expected bool 14 | }{ 15 | {[]int{}, []int{}, true}, 16 | {[]int{1}, []int{1}, true}, 17 | {[]int{}, []int{1}, false}, 18 | {[]int{1, 2}, []int{1, 2}, true}, 19 | {[]int{1, 2}, []int{2, 1}, false}, 20 | {[]int{1, 2, 3}, []int{1, 2}, false}, 21 | } 22 | for _, test := range tests { 23 | first := NewComparableFromSlice(test.first) 24 | second := NewComparableFromSlice(test.second) 25 | if first.Equal(second) != test.expected { 26 | t.Errorf("Equal(%v, %v) = %v, expected %v", 27 | test.first, test.second, first.Equal(second), test.expected, 28 | ) 29 | } 30 | } 31 | } 32 | 33 | func TestCompact(t *testing.T) { 34 | tests := []struct { 35 | slice []int 36 | expected []int 37 | }{ 38 | {[]int{}, []int{}}, 39 | {[]int{1}, []int{1}}, 40 | {[]int{1, 2}, []int{1, 2}}, 41 | {[]int{1, 1, 2}, []int{1, 2}}, 42 | {[]int{1, 2, 1}, []int{1, 2, 1}}, 43 | {[]int{1, 1, 1}, []int{1}}, 44 | } 45 | for _, test := range tests { 46 | list := NewComparableFromSlice(test.slice) 47 | list.Compact() 48 | testutils.ExpectSlice(t, test.expected, list.ToSlice()) 49 | } 50 | } 51 | 52 | func TestIndex(t *testing.T) { 53 | tests := []struct { 54 | slice []int 55 | value int 56 | expected int 57 | }{ 58 | {[]int{}, 1, -1}, 59 | {[]int{1}, 1, 0}, 60 | {[]int{1, 1}, 1, 0}, 61 | {[]int{1, 1}, 2, -1}, 62 | {[]int{1, 2, 3}, 2, 1}, 63 | {[]int{1, 2, 3}, 3, 2}, 64 | } 65 | for _, test := range tests { 66 | list := NewComparableFromSlice(test.slice) 67 | 68 | if list.Index(test.value) != test.expected { 69 | t.Errorf("Index(%v, %v) = %v, expected %v", test.slice, test.value, list.Index(test.value), test.expected) 70 | } 71 | } 72 | } 73 | 74 | func TestIndexFunc(t *testing.T) { 75 | tests := []struct { 76 | slice []int 77 | f func(value int) bool 78 | expected int 79 | }{ 80 | {[]int{}, func(value int) bool { return true }, -1}, 81 | {[]int{1}, func(value int) bool { return true }, 0}, 82 | {[]int{1, 1}, func(value int) bool { return true }, 0}, 83 | {[]int{1, 1}, func(value int) bool { return false }, -1}, 84 | {[]int{1, 2, 3}, func(value int) bool { return value == 2 }, 1}, 85 | {[]int{1, 2, 3}, func(value int) bool { return value == 3 }, 2}, 86 | } 87 | for _, test := range tests { 88 | list := NewComparableFromSlice(test.slice) 89 | 90 | if list.IndexFunc(test.f) != test.expected { 91 | t.Errorf("IndexFunc(%v, func) = %v, expected %v", test.slice, list.IndexFunc(test.f), test.expected) 92 | } 93 | } 94 | } 95 | 96 | func TestContains(t *testing.T) { 97 | tests := []struct { 98 | slice []int 99 | value int 100 | expected bool 101 | }{ 102 | {[]int{}, 1, false}, 103 | {[]int{1}, 1, true}, 104 | {[]int{1, 1}, 1, true}, 105 | {[]int{1, 1}, 2, false}, 106 | {[]int{1, 2, 3}, 2, true}, 107 | {[]int{1, 2, 3}, 3, true}, 108 | } 109 | for _, test := range tests { 110 | list := NewComparableFromSlice(test.slice) 111 | 112 | if list.Contains(test.value) != test.expected { 113 | t.Errorf("Contains(%v, %v) = %v, expected %v", test.slice, test.value, list.Contains(test.value), test.expected) 114 | } 115 | } 116 | } 117 | 118 | func TestContainsFunc(t *testing.T) { 119 | tests := []struct { 120 | slice []int 121 | f func(value int) bool 122 | expected bool 123 | }{ 124 | {[]int{}, func(value int) bool { return true }, false}, 125 | {[]int{1}, func(value int) bool { return true }, true}, 126 | {[]int{1, 1}, func(value int) bool { return true }, true}, 127 | {[]int{1, 1}, func(value int) bool { return false }, false}, 128 | {[]int{1, 2, 3}, func(value int) bool { return value == 2 }, true}, 129 | {[]int{1, 2, 3}, func(value int) bool { return value == 3 }, true}, 130 | } 131 | for _, test := range tests { 132 | list := NewComparableFromSlice(test.slice) 133 | 134 | if list.ContainsFunc(test.f) != test.expected { 135 | t.Errorf("ContainsFunc(%v, func) = %v, expected %v", test.slice, list.ContainsFunc(test.f), test.expected) 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /list/list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import "github.com/jesseduffield/generics/slices" 4 | 5 | // List is a struct which wraps a slice and provides convenience methods for it. 6 | // Unfortunately due to some limitations in Go's type system, certain methods 7 | // are not available e.g. Map. 8 | 9 | type List[T any] struct { 10 | slice []T 11 | } 12 | 13 | func New[T any]() *List[T] { 14 | return &List[T]{} 15 | } 16 | 17 | func NewFromSlice[T any](slice []T) *List[T] { 18 | return &List[T]{slice: slice} 19 | } 20 | 21 | func (l *List[T]) ToSlice() []T { 22 | return l.slice 23 | } 24 | 25 | // Mutative methods 26 | 27 | func (l *List[T]) Push(v T) { 28 | l.slice = append(l.slice, v) 29 | } 30 | 31 | func (l *List[T]) Pop() T { 32 | var value T 33 | value, l.slice = slices.Pop(l.slice) 34 | return value 35 | } 36 | 37 | func (l *List[T]) Insert(index int, values ...T) { 38 | l.slice = slices.Insert(l.slice, index, values...) 39 | } 40 | 41 | func (l *List[T]) Append(values ...T) { 42 | l.slice = append(l.slice, values...) 43 | } 44 | 45 | func (l *List[T]) Prepend(values ...T) { 46 | l.slice = append(values, l.slice...) 47 | } 48 | 49 | func (l *List[T]) Remove(index int) { 50 | l.Delete(index, index+1) 51 | } 52 | 53 | func (l *List[T]) Delete(from int, to int) { 54 | l.slice = slices.Delete(l.slice, from, to) 55 | } 56 | 57 | func (l *List[T]) FilterInPlace(test func(value T) bool) { 58 | l.slice = slices.FilterInPlace(l.slice, test) 59 | } 60 | 61 | func (l *List[T]) MapInPlace(f func(value T) T) { 62 | slices.MapInPlace(l.slice, f) 63 | } 64 | 65 | func (l *List[T]) ReverseInPlace() { 66 | slices.ReverseInPlace(l.slice) 67 | } 68 | 69 | // Non-mutative methods 70 | 71 | // Similar to Append but we leave the original slice untouched and return a new list 72 | func (l *List[T]) Concat(values ...T) *List[T] { 73 | return NewFromSlice(slices.Concat(l.slice, values...)) 74 | } 75 | 76 | func (l *List[T]) Filter(test func(value T) bool) *List[T] { 77 | return NewFromSlice(slices.Filter(l.slice, test)) 78 | } 79 | 80 | // Unfortunately this does not support mapping from one type to another 81 | // because Go does not yet (and may never) support methods defining their own 82 | // type parameters. For that functionality you'll need to use the standalone 83 | // Map function instead 84 | func (l *List[T]) Map(f func(value T) T) *List[T] { 85 | return NewFromSlice(slices.Map(l.slice, f)) 86 | } 87 | 88 | func (l *List[T]) Clone() *List[T] { 89 | return NewFromSlice(slices.Clone(l.slice)) 90 | } 91 | 92 | func (l *List[T]) Some(test func(value T) bool) bool { 93 | return slices.Some(l.slice, test) 94 | } 95 | 96 | func (l *List[T]) Every(test func(value T) bool) bool { 97 | return slices.Every(l.slice, test) 98 | } 99 | 100 | func (l *List[T]) IndexFunc(f func(T) bool) int { 101 | return slices.IndexFunc(l.slice, f) 102 | } 103 | 104 | func (l *List[T]) ContainsFunc(f func(T) bool) bool { 105 | return slices.ContainsFunc(l.slice, f) 106 | } 107 | 108 | func (l *List[T]) Reverse() *List[T] { 109 | return NewFromSlice(slices.Reverse(l.slice)) 110 | } 111 | 112 | func (l *List[T]) IsEmpty() bool { 113 | return len(l.slice) == 0 114 | } 115 | 116 | func (l *List[T]) Len() int { 117 | return len(l.slice) 118 | } 119 | 120 | func (l *List[T]) Get(index int) T { 121 | return l.slice[index] 122 | } 123 | -------------------------------------------------------------------------------- /list/list_test.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jesseduffield/generics/internal/testutils" 7 | "github.com/jesseduffield/generics/slices" 8 | ) 9 | 10 | func TestPush(t *testing.T) { 11 | list := New[int]() 12 | list.Push(1) 13 | list.Push(2) 14 | slice := list.ToSlice() 15 | testutils.ExpectSlice(t, []int{1, 2}, slice) 16 | } 17 | 18 | func TestInsert(t *testing.T) { 19 | tests := []struct { 20 | startSlice []int 21 | index int 22 | values []int 23 | endSlice []int 24 | }{ 25 | {[]int{}, 0, []int{1}, []int{1}}, 26 | {[]int{1}, 0, []int{2}, []int{2, 1}}, 27 | {[]int{1, 2}, 1, []int{3}, []int{1, 3, 2}}, 28 | {[]int{1, 2}, 2, []int{3}, []int{1, 2, 3}}, 29 | {[]int{1, 2}, 2, []int{3, 4}, []int{1, 2, 3, 4}}, 30 | {[]int{1, 2}, 1, []int{3, 4}, []int{1, 3, 4, 2}}, 31 | } 32 | for _, test := range tests { 33 | list := NewFromSlice(test.startSlice) 34 | list.Insert(test.index, test.values...) 35 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 36 | } 37 | 38 | panicTests := []struct { 39 | startSlice []int 40 | index int 41 | value int 42 | }{ 43 | {[]int{}, 1, 1}, 44 | {[]int{}, 2, 1}, 45 | {[]int{1}, 2, 1}, 46 | {[]int{1}, -1, 1}, 47 | } 48 | for _, test := range panicTests { 49 | func() { 50 | defer testutils.ExpectPanic(t) 51 | list := NewFromSlice(test.startSlice) 52 | list.Insert(test.index, test.value) 53 | }() 54 | } 55 | } 56 | 57 | func TestRemove(t *testing.T) { 58 | tests := []struct { 59 | startSlice []int 60 | index int 61 | endSlice []int 62 | }{ 63 | {[]int{1}, 0, []int{}}, 64 | {[]int{1, 2}, 0, []int{2}}, 65 | {[]int{1, 2}, 1, []int{1}}, 66 | } 67 | for _, test := range tests { 68 | list := NewFromSlice(test.startSlice) 69 | list.Remove(test.index) 70 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 71 | } 72 | 73 | panicTests := []struct { 74 | startSlice []int 75 | index int 76 | }{ 77 | {[]int{}, 0}, 78 | {[]int{}, 1}, 79 | {[]int{1}, 1}, 80 | {[]int{1}, -1}, 81 | } 82 | for _, test := range panicTests { 83 | func() { 84 | defer testutils.ExpectPanic(t) 85 | list := NewFromSlice(test.startSlice) 86 | list.Remove(test.index) 87 | }() 88 | } 89 | } 90 | 91 | func TestPop(t *testing.T) { 92 | tests := []struct { 93 | startSlice []int 94 | endSlice []int 95 | }{ 96 | {[]int{1}, []int{}}, 97 | {[]int{1, 2}, []int{1}}, 98 | {[]int{1, 2, 3}, []int{1, 2}}, 99 | } 100 | for _, test := range tests { 101 | list := NewFromSlice(test.startSlice) 102 | list.Pop() 103 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 104 | } 105 | 106 | panicTests := []struct { 107 | startSlice []int 108 | }{ 109 | {[]int{}}, 110 | } 111 | for _, test := range panicTests { 112 | func() { 113 | defer testutils.ExpectPanic(t) 114 | list := NewFromSlice(test.startSlice) 115 | list.Pop() 116 | }() 117 | } 118 | } 119 | 120 | func TestFilter(t *testing.T) { 121 | even := func(value int) bool { return value%2 == 0 } 122 | tests := []struct { 123 | startSlice []int 124 | testFunc func(value int) bool 125 | endSlice []int 126 | }{ 127 | {[]int{1}, even, []int{}}, 128 | {[]int{1, 2}, even, []int{2}}, 129 | {[]int{1, 2, 3}, even, []int{2}}, 130 | {[]int{1, 2, 3, 4}, even, []int{2, 4}}, 131 | } 132 | for _, test := range tests { 133 | testSlice := slices.Clone(test.startSlice) 134 | list := NewFromSlice(test.startSlice) 135 | testutils.ExpectSlice(t, test.endSlice, list.Filter(test.testFunc).ToSlice()) 136 | testutils.ExpectSlice(t, testSlice, list.ToSlice()) 137 | } 138 | } 139 | 140 | func TestFilterInPlace(t *testing.T) { 141 | even := func(value int) bool { return value%2 == 0 } 142 | tests := []struct { 143 | startSlice []int 144 | testFunc func(value int) bool 145 | endSlice []int 146 | }{ 147 | {[]int{1}, even, []int{}}, 148 | {[]int{1, 2}, even, []int{2}}, 149 | {[]int{1, 2, 3}, even, []int{2}}, 150 | {[]int{1, 2, 3, 4}, even, []int{2, 4}}, 151 | } 152 | 153 | for _, test := range tests { 154 | list := NewFromSlice(test.startSlice) 155 | list.FilterInPlace(test.testFunc) 156 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 157 | } 158 | } 159 | 160 | func TestMap(t *testing.T) { 161 | double := func(value int) int { return value * 2 } 162 | tests := []struct { 163 | startSlice []int 164 | mapFunc func(value int) int 165 | endSlice []int 166 | }{ 167 | {[]int{}, double, []int{}}, 168 | {[]int{1}, double, []int{2}}, 169 | {[]int{1, 2}, double, []int{2, 4}}, 170 | {[]int{1, 2, 3}, double, []int{2, 4, 6}}, 171 | {[]int{1, 2, 3, 4}, double, []int{2, 4, 6, 8}}, 172 | } 173 | 174 | for _, test := range tests { 175 | testSlice := slices.Clone(test.startSlice) 176 | list := NewFromSlice(test.startSlice) 177 | testutils.ExpectSlice(t, test.endSlice, list.Map(test.mapFunc).ToSlice()) 178 | testutils.ExpectSlice(t, testSlice, list.ToSlice()) 179 | } 180 | } 181 | 182 | func TestMapInPlace(t *testing.T) { 183 | double := func(value int) int { return value * 2 } 184 | tests := []struct { 185 | startSlice []int 186 | mapFunc func(value int) int 187 | endSlice []int 188 | }{ 189 | {[]int{}, double, []int{}}, 190 | {[]int{1}, double, []int{2}}, 191 | {[]int{1, 2}, double, []int{2, 4}}, 192 | {[]int{1, 2, 3}, double, []int{2, 4, 6}}, 193 | {[]int{1, 2, 3, 4}, double, []int{2, 4, 6, 8}}, 194 | } 195 | for _, test := range tests { 196 | list := NewFromSlice(test.startSlice) 197 | list.MapInPlace(test.mapFunc) 198 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 199 | } 200 | } 201 | 202 | func TestAppend(t *testing.T) { 203 | tests := []struct { 204 | startSlice []int 205 | other []int 206 | endSlice []int 207 | }{ 208 | {[]int{}, []int{}, []int{}}, 209 | {[]int{1}, []int{2}, []int{1, 2}}, 210 | {[]int{1, 2}, []int{3, 4}, []int{1, 2, 3, 4}}, 211 | } 212 | for _, test := range tests { 213 | list := NewFromSlice(test.startSlice) 214 | list.Append(test.other...) 215 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 216 | } 217 | } 218 | 219 | func TestPrepend(t *testing.T) { 220 | tests := []struct { 221 | startSlice []int 222 | values []int 223 | endSlice []int 224 | }{ 225 | {[]int{}, []int{}, []int{}}, 226 | {[]int{}, []int{1}, []int{1}}, 227 | {[]int{1}, []int{2}, []int{2, 1}}, 228 | {[]int{1, 2}, []int{3}, []int{3, 1, 2}}, 229 | {[]int{1, 2}, []int{3, 4}, []int{3, 4, 1, 2}}, 230 | } 231 | for _, test := range tests { 232 | list := NewFromSlice(test.startSlice) 233 | list.Prepend(test.values...) 234 | testutils.ExpectSlice(t, test.endSlice, list.ToSlice()) 235 | } 236 | } 237 | 238 | func TestConcat(t *testing.T) { 239 | tests := []struct { 240 | startSlice []int 241 | other []int 242 | endSlice []int 243 | }{ 244 | {[]int{}, []int{}, []int{}}, 245 | {[]int{1}, []int{2}, []int{1, 2}}, 246 | {[]int{1, 2}, []int{3, 4}, []int{1, 2, 3, 4}}, 247 | } 248 | for _, test := range tests { 249 | testSlice := slices.Clone(test.startSlice) 250 | list := NewFromSlice(test.startSlice) 251 | result := list.Concat(test.other...) 252 | testutils.ExpectSlice(t, test.endSlice, result.ToSlice()) 253 | testutils.ExpectSlice(t, testSlice, list.ToSlice()) 254 | } 255 | } 256 | 257 | func TestSome(t *testing.T) { 258 | even := func(value int) bool { return value%2 == 0 } 259 | tests := []struct { 260 | startSlice []int 261 | testFunc func(value int) bool 262 | expected bool 263 | }{ 264 | {[]int{}, even, false}, 265 | {[]int{1}, even, false}, 266 | {[]int{2}, even, true}, 267 | {[]int{1, 2}, even, true}, 268 | } 269 | for _, test := range tests { 270 | list := NewFromSlice(test.startSlice) 271 | if list.Some(test.testFunc) != test.expected { 272 | t.Errorf("Some(%v) = %v, expected %v", 273 | test.startSlice, list.Some(test.testFunc), test.expected, 274 | ) 275 | } 276 | } 277 | } 278 | 279 | func TestEvery(t *testing.T) { 280 | even := func(value int) bool { return value%2 == 0 } 281 | tests := []struct { 282 | startSlice []int 283 | testFunc func(value int) bool 284 | expected bool 285 | }{ 286 | {[]int{}, even, true}, 287 | {[]int{1}, even, false}, 288 | {[]int{2}, even, true}, 289 | {[]int{1, 2}, even, false}, 290 | {[]int{2, 2}, even, true}, 291 | } 292 | for _, test := range tests { 293 | list := NewFromSlice(test.startSlice) 294 | if list.Every(test.testFunc) != test.expected { 295 | t.Errorf("Every(%v) = %v, expected %v", 296 | test.startSlice, list.Every(test.testFunc), test.expected, 297 | ) 298 | } 299 | } 300 | } 301 | 302 | func TestReverse(t *testing.T) { 303 | tests := []struct { 304 | startSlice []int 305 | expected []int 306 | }{ 307 | {[]int{}, []int{}}, 308 | {[]int{1}, []int{1}}, 309 | {[]int{1, 2}, []int{2, 1}}, 310 | {[]int{1, 2, 3}, []int{3, 2, 1}}, 311 | } 312 | for _, test := range tests { 313 | list := NewFromSlice(test.startSlice) 314 | testutils.ExpectSlice(t, test.expected, list.Reverse().ToSlice()) 315 | } 316 | } 317 | 318 | func TestReverseInPlace(t *testing.T) { 319 | tests := []struct { 320 | startSlice []int 321 | expected []int 322 | }{ 323 | {[]int{}, []int{}}, 324 | {[]int{1}, []int{1}}, 325 | {[]int{1, 2}, []int{2, 1}}, 326 | {[]int{1, 2, 3}, []int{3, 2, 1}}, 327 | } 328 | for _, test := range tests { 329 | list := NewFromSlice(test.startSlice) 330 | list.ReverseInPlace() 331 | testutils.ExpectSlice(t, test.expected, list.ToSlice()) 332 | } 333 | } 334 | 335 | func TestClone(t *testing.T) { 336 | list := NewFromSlice([]int{1, 2, 3}) 337 | clone := list.Clone() 338 | testutils.ExpectSlice(t, []int{1, 2, 3}, clone.ToSlice()) 339 | 340 | // ensure that the clone has its own slice 341 | clone.Insert(0, 1) 342 | testutils.ExpectSlice(t, []int{1, 1, 2, 3}, clone.ToSlice()) 343 | testutils.ExpectSlice(t, []int{1, 2, 3}, list.ToSlice()) 344 | } 345 | 346 | func TestFilterAndMap(t *testing.T) { 347 | list := NewFromSlice([]int{1, 2, 3, 4}) 348 | result := list. 349 | Filter(func(value int) bool { return value%2 == 0 }). 350 | Map(func(value int) int { return value * 2 }) 351 | 352 | testutils.ExpectSlice(t, []int{4, 8}, result.ToSlice()) 353 | } 354 | 355 | func TestIsEmpty(t *testing.T) { 356 | list := NewFromSlice([]int{}) 357 | if !list.IsEmpty() { 358 | t.Errorf("IsEmpty() = %v, expected %v", list.IsEmpty(), true) 359 | } 360 | 361 | list = NewFromSlice([]int{1}) 362 | if list.IsEmpty() { 363 | t.Errorf("IsEmpty() = %v, expected %v", list.IsEmpty(), false) 364 | } 365 | } 366 | 367 | func TestLen(t *testing.T) { 368 | list := NewFromSlice([]int{}) 369 | if list.Len() != 0 { 370 | t.Errorf("Len() = %v, expected %v", list.Len(), 0) 371 | } 372 | 373 | list = NewFromSlice([]int{1}) 374 | if list.Len() != 1 { 375 | t.Errorf("Len() = %v, expected %v", list.Len(), 1) 376 | } 377 | } 378 | 379 | func TestGet(t *testing.T) { 380 | list := NewFromSlice([]int{1, 2, 3}) 381 | if list.Get(0) != 1 { 382 | t.Errorf("Get(0) = %v, expected %v", list.Get(0), 1) 383 | } 384 | if list.Get(1) != 2 { 385 | t.Errorf("Get(1) = %v, expected %v", list.Get(1), 2) 386 | } 387 | if list.Get(2) != 3 { 388 | t.Errorf("Get(2) = %v, expected %v", list.Get(2), 3) 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /maps/maps.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | func Keys[Key comparable, Value any](m map[Key]Value) []Key { 4 | keys := make([]Key, 0, len(m)) 5 | for key := range m { 6 | keys = append(keys, key) 7 | } 8 | return keys 9 | } 10 | 11 | func Values[Key comparable, Value any](m map[Key]Value) []Value { 12 | values := make([]Value, 0, len(m)) 13 | for _, value := range m { 14 | values = append(values, value) 15 | } 16 | return values 17 | } 18 | 19 | func TransformValues[Key comparable, Value any, NewValue any]( 20 | m map[Key]Value, fn func(Value) NewValue, 21 | ) map[Key]NewValue { 22 | output := make(map[Key]NewValue, len(m)) 23 | for key, value := range m { 24 | output[key] = fn(value) 25 | } 26 | return output 27 | } 28 | 29 | func TransformKeys[Key comparable, Value any, NewKey comparable](m map[Key]Value, fn func(Key) NewKey) map[NewKey]Value { 30 | output := make(map[NewKey]Value, len(m)) 31 | for key, value := range m { 32 | output[fn(key)] = value 33 | } 34 | return output 35 | } 36 | 37 | func MapToSlice[Key comparable, Value any, Mapped any](m map[Key]Value, f func(Key, Value) Mapped) []Mapped { 38 | output := make([]Mapped, 0, len(m)) 39 | for key, value := range m { 40 | output = append(output, f(key, value)) 41 | } 42 | return output 43 | } 44 | 45 | func Filter[Key comparable, Value any](m map[Key]Value, f func(Key, Value) bool) map[Key]Value { 46 | output := map[Key]Value{} 47 | for key, value := range m { 48 | if f(key, value) { 49 | output[key] = value 50 | } 51 | } 52 | return output 53 | } 54 | -------------------------------------------------------------------------------- /maps/maps_test.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/jesseduffield/generics/internal/testutils" 8 | ) 9 | 10 | func TestKeys(t *testing.T) { 11 | tests := []struct { 12 | hashMap map[string]int 13 | expected []string 14 | }{ 15 | {map[string]int{}, []string{}}, 16 | {map[string]int{"a": 1}, []string{"a"}}, 17 | } 18 | for _, test := range tests { 19 | testutils.ExpectSlice(t, test.expected, Keys(test.hashMap)) 20 | } 21 | } 22 | 23 | func TestValues(t *testing.T) { 24 | tests := []struct { 25 | hashMap map[string]int 26 | expected []int 27 | }{ 28 | {map[string]int{}, []int{}}, 29 | {map[string]int{"a": 1}, []int{1}}, 30 | } 31 | for _, test := range tests { 32 | testutils.ExpectSlice(t, test.expected, Values(test.hashMap)) 33 | } 34 | } 35 | 36 | func TestTransformKeys(t *testing.T) { 37 | tests := []struct { 38 | hashMap map[int]string 39 | transform func(int) int64 40 | expected map[int64]string 41 | }{ 42 | { 43 | hashMap: map[int]string{}, 44 | transform: func(i int) int64 { return (int64)(i) }, 45 | expected: map[int64]string{}, 46 | }, 47 | { 48 | hashMap: map[int]string{1: "a"}, 49 | transform: func(i int) int64 { return 2 * (int64)(i) }, 50 | expected: map[int64]string{2: "a"}, 51 | }, 52 | } 53 | for _, test := range tests { 54 | testutils.ExpectMap(t, test.expected, TransformKeys(test.hashMap, test.transform)) 55 | } 56 | } 57 | 58 | func TestTransformValues(t *testing.T) { 59 | tests := []struct { 60 | hashMap map[string]int 61 | transform func(int) int64 62 | expected map[string]int64 63 | }{ 64 | { 65 | hashMap: map[string]int{}, 66 | transform: func(i int) int64 { return (int64)(i) }, 67 | expected: map[string]int64{}, 68 | }, 69 | { 70 | hashMap: map[string]int{"a": 1}, 71 | transform: func(i int) int64 { return 2 * (int64)(i) }, 72 | expected: map[string]int64{"a": 2}, 73 | }, 74 | } 75 | for _, test := range tests { 76 | testutils.ExpectMap(t, test.expected, TransformValues(test.hashMap, test.transform)) 77 | } 78 | } 79 | 80 | func TestMapToSlice(t *testing.T) { 81 | tests := []struct { 82 | hashMap map[int64]int 83 | f func(int64, int) string 84 | expected []string 85 | }{ 86 | { 87 | hashMap: map[int64]int{}, 88 | f: func(k int64, v int) string { return fmt.Sprintf("%d:%d", k, v) }, 89 | expected: []string{}, 90 | }, 91 | { 92 | hashMap: map[int64]int{2: 5}, 93 | f: func(k int64, v int) string { return fmt.Sprintf("%d:%d", k, v) }, 94 | expected: []string{"2:5"}, 95 | }, 96 | { 97 | hashMap: map[int64]int{2: 5, 3: 4}, 98 | f: func(k int64, v int) string { return fmt.Sprintf("%d:%d", k, v) }, 99 | expected: []string{"2:5", "3:4"}, 100 | }, 101 | } 102 | for _, test := range tests { 103 | testutils.ExpectSlice(t, test.expected, MapToSlice(test.hashMap, test.f)) 104 | } 105 | } 106 | 107 | func TestFilter(t *testing.T) { 108 | tests := []struct { 109 | hashMap map[int64]int 110 | f func(int64, int) bool 111 | expected map[int64]int 112 | }{ 113 | { 114 | hashMap: map[int64]int{}, 115 | f: func(k int64, v int) bool { return int(k)+v > 0 }, 116 | expected: map[int64]int{}, 117 | }, 118 | { 119 | hashMap: map[int64]int{2: 5}, 120 | f: func(k int64, v int) bool { return int(k)+v > 0 }, 121 | expected: map[int64]int{2: 5}, 122 | }, 123 | { 124 | hashMap: map[int64]int{2: 5, 3: -4}, 125 | f: func(k int64, v int) bool { return int(k)+v > 0 }, 126 | expected: map[int64]int{2: 5}, 127 | }, 128 | } 129 | for _, test := range tests { 130 | testutils.ExpectMap(t, test.expected, Filter(test.hashMap, test.f)) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /orderedset/orderedset.go: -------------------------------------------------------------------------------- 1 | package orderedset 2 | 3 | import ( 4 | orderedmap "github.com/wk8/go-ordered-map/v2" 5 | ) 6 | 7 | type OrderedSet[T comparable] struct { 8 | om *orderedmap.OrderedMap[T, bool] 9 | } 10 | 11 | func New[T comparable]() *OrderedSet[T] { 12 | return &OrderedSet[T]{om: orderedmap.New[T, bool]()} 13 | } 14 | 15 | func NewFromSlice[T comparable](slice []T) *OrderedSet[T] { 16 | result := &OrderedSet[T]{om: orderedmap.New[T, bool](len(slice))} 17 | result.Add(slice...) 18 | return result 19 | } 20 | 21 | func (os *OrderedSet[T]) Add(values ...T) { 22 | for _, value := range values { 23 | os.om.Set(value, true) 24 | } 25 | } 26 | 27 | func (os *OrderedSet[T]) Remove(value T) { 28 | os.om.Delete(value) 29 | } 30 | 31 | func (os *OrderedSet[T]) RemoveSlice(slice []T) { 32 | for _, value := range slice { 33 | os.Remove(value) 34 | } 35 | } 36 | 37 | func (os *OrderedSet[T]) Includes(value T) bool { 38 | return os.om.Value(value) 39 | } 40 | 41 | func (os *OrderedSet[T]) Len() int { 42 | return os.om.Len() 43 | } 44 | 45 | func (os *OrderedSet[T]) ToSliceFromOldest() []T { 46 | // TODO: can be simplified to 47 | // return os.om.KeysFromOldest() 48 | // when we update to a newer version of go-ordered-map 49 | result := make([]T, 0, os.Len()) 50 | for pair := os.om.Oldest(); pair != nil; pair = pair.Next() { 51 | result = append(result, pair.Key) 52 | } 53 | return result 54 | } 55 | 56 | func (os *OrderedSet[T]) ToSliceFromNewest() []T { 57 | // TODO: can be simplified to 58 | // return os.om.KeysFromNewest() 59 | // when we update to a newer version of go-ordered-map 60 | result := make([]T, 0, os.Len()) 61 | for pair := os.om.Newest(); pair != nil; pair = pair.Prev() { 62 | result = append(result, pair.Key) 63 | } 64 | return result 65 | } 66 | -------------------------------------------------------------------------------- /orderedset/orderedset_test.go: -------------------------------------------------------------------------------- 1 | package orderedset 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jesseduffield/generics/internal/testutils" 7 | ) 8 | 9 | func TestAddIncludes(t *testing.T) { 10 | os := New[int]() 11 | os.Add(1) 12 | if !os.Includes(1) { 13 | t.Errorf("Add(1) failed: Includes(1) returned false") 14 | } 15 | 16 | if os.Includes(0) { 17 | t.Errorf("Add(1) failed: Includes(0) returned true") 18 | } 19 | } 20 | 21 | func TestAddSliceIncludes(t *testing.T) { 22 | os := New[int]() 23 | os.Add(1, 2) 24 | if !os.Includes(1) { 25 | t.Errorf("AddSlice failed: Includes(1) returned false") 26 | } 27 | 28 | if !os.Includes(2) { 29 | t.Errorf("AddSlice failed: Includes(2) returned false") 30 | } 31 | 32 | if os.Includes(3) { 33 | t.Errorf("AddSlice failed: Includes(3) returned true") 34 | } 35 | } 36 | 37 | func TestRemoveIncludes(t *testing.T) { 38 | os := NewFromSlice([]int{1, 2}) 39 | os.Remove(1) 40 | if os.Includes(1) { 41 | t.Errorf("Remove failed: Includes(1) returned true") 42 | } 43 | 44 | if !os.Includes(2) { 45 | t.Errorf("Remove failed: Includes(2) returned false") 46 | } 47 | } 48 | 49 | func TestRemoveSliceIncludes(t *testing.T) { 50 | os := NewFromSlice([]int{1, 2, 3}) 51 | os.RemoveSlice([]int{1, 2}) 52 | if os.Includes(1) { 53 | t.Errorf("Remove failed: Includes(1) returned true") 54 | } 55 | 56 | if os.Includes(2) { 57 | t.Errorf("Remove failed: Includes(2) returned true") 58 | } 59 | 60 | if !os.Includes(3) { 61 | t.Errorf("Remove failed: Includes(3) returned false") 62 | } 63 | } 64 | 65 | func TestNewFromSlice(t *testing.T) { 66 | os := NewFromSlice([]int{1, 2}) 67 | if !os.Includes(1) { 68 | t.Errorf("NewFromSlice failed: Includes(1) returned false") 69 | } 70 | 71 | if !os.Includes(2) { 72 | t.Errorf("NewFromSlice failed: Includes(2) returned false") 73 | } 74 | 75 | if os.Includes(3) { 76 | t.Errorf("NewFromSlice failed: Includes(3) returned true") 77 | } 78 | } 79 | 80 | func TestLen(t *testing.T) { 81 | set := NewFromSlice([]int{}) 82 | if set.Len() != 0 { 83 | t.Errorf("Len() = %v, expected %v", set.Len(), 0) 84 | } 85 | 86 | set = NewFromSlice([]int{1}) 87 | if set.Len() != 1 { 88 | t.Errorf("Len() = %v, expected %v", set.Len(), 1) 89 | } 90 | } 91 | 92 | func TestToSlice(t *testing.T) { 93 | set := New[int]() 94 | set.Add(1) 95 | set.Add(3) 96 | set.Add(2) 97 | 98 | slice := set.ToSliceFromOldest() 99 | testutils.ExpectSlice(t, []int{1, 3, 2}, slice) 100 | 101 | slice = set.ToSliceFromNewest() 102 | testutils.ExpectSlice(t, []int{2, 3, 1}, slice) 103 | } 104 | -------------------------------------------------------------------------------- /set/set.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import "github.com/jesseduffield/generics/maps" 4 | 5 | type Set[T comparable] struct { 6 | hashMap map[T]bool 7 | } 8 | 9 | func New[T comparable]() *Set[T] { 10 | return &Set[T]{hashMap: make(map[T]bool)} 11 | } 12 | 13 | func NewFromSlice[T comparable](slice []T) *Set[T] { 14 | result := &Set[T]{hashMap: make(map[T]bool, len(slice))} 15 | result.Add(slice...) 16 | return result 17 | } 18 | 19 | func (s *Set[T]) Add(values ...T) { 20 | for _, value := range values { 21 | s.hashMap[value] = true 22 | } 23 | } 24 | 25 | func (s *Set[T]) Remove(value T) { 26 | delete(s.hashMap, value) 27 | } 28 | 29 | func (s *Set[T]) RemoveSlice(slice []T) { 30 | for _, value := range slice { 31 | s.Remove(value) 32 | } 33 | } 34 | 35 | func (s *Set[T]) Includes(value T) bool { 36 | return s.hashMap[value] 37 | } 38 | 39 | func (s *Set[T]) Len() int { 40 | return len(s.hashMap) 41 | } 42 | 43 | // output slice is not necessarily in the same order that items were added 44 | func (s *Set[T]) ToSlice() []T { 45 | return maps.Keys(s.hashMap) 46 | } 47 | -------------------------------------------------------------------------------- /set/set_test.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import "testing" 4 | 5 | func TestAddIncludes(t *testing.T) { 6 | set := New[int]() 7 | set.Add(1) 8 | if !set.Includes(1) { 9 | t.Errorf("Add(1) failed: Includes(1) returned false") 10 | } 11 | 12 | if set.Includes(0) { 13 | t.Errorf("Add(1) failed: Includes(0) returned true") 14 | } 15 | } 16 | 17 | func TestAddSliceIncludes(t *testing.T) { 18 | set := New[int]() 19 | set.Add(1, 2) 20 | if !set.Includes(1) { 21 | t.Errorf("AddSlice failed: Includes(1) returned false") 22 | } 23 | 24 | if !set.Includes(2) { 25 | t.Errorf("AddSlice failed: Includes(2) returned false") 26 | } 27 | 28 | if set.Includes(3) { 29 | t.Errorf("AddSlice failed: Includes(3) returned true") 30 | } 31 | } 32 | 33 | func TestRemoveIncludes(t *testing.T) { 34 | set := NewFromSlice([]int{1, 2}) 35 | set.Remove(1) 36 | if set.Includes(1) { 37 | t.Errorf("Remove failed: Includes(1) returned true") 38 | } 39 | 40 | if !set.Includes(2) { 41 | t.Errorf("Remove failed: Includes(2) returned false") 42 | } 43 | } 44 | 45 | func TestRemoveSliceIncludes(t *testing.T) { 46 | set := NewFromSlice([]int{1, 2, 3}) 47 | set.RemoveSlice([]int{1, 2}) 48 | if set.Includes(1) { 49 | t.Errorf("Remove failed: Includes(1) returned true") 50 | } 51 | 52 | if set.Includes(2) { 53 | t.Errorf("Remove failed: Includes(2) returned true") 54 | } 55 | 56 | if !set.Includes(3) { 57 | t.Errorf("Remove failed: Includes(3) returned false") 58 | } 59 | } 60 | 61 | func TestNewFromSlice(t *testing.T) { 62 | set := NewFromSlice([]int{1, 2}) 63 | if !set.Includes(1) { 64 | t.Errorf("NewFromSlice failed: Includes(1) returned false") 65 | } 66 | 67 | if !set.Includes(2) { 68 | t.Errorf("NewFromSlice failed: Includes(2) returned false") 69 | } 70 | 71 | if set.Includes(3) { 72 | t.Errorf("NewFromSlice failed: Includes(3) returned true") 73 | } 74 | } 75 | 76 | func TestLen(t *testing.T) { 77 | set := NewFromSlice([]int{}) 78 | if set.Len() != 0 { 79 | t.Errorf("Len() = %v, expected %v", set.Len(), 0) 80 | } 81 | 82 | set = NewFromSlice([]int{1}) 83 | if set.Len() != 1 { 84 | t.Errorf("Len() = %v, expected %v", set.Len(), 1) 85 | } 86 | } 87 | 88 | func TestToSlice(t *testing.T) { 89 | set := New[int]() 90 | set.Add(1) 91 | set.Add(2) 92 | 93 | slice := set.ToSlice() 94 | if len(slice) != 2 { 95 | t.Errorf("ToSlice failed: len(slice) = %d, expected 2", len(slice)) 96 | } 97 | 98 | if !((slice[0] == 1 && slice[1] == 2) || (slice[0] == 2 && slice[1] == 1)) { 99 | t.Errorf("ToSlice failed: expected 1 and 2 in slice in any order, got %v", slice) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /slices/delegated_slices.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "golang.org/x/exp/constraints" 5 | "golang.org/x/exp/slices" 6 | ) 7 | 8 | // This file delegates to the official slices package, so that we end up with a superset of the official API. 9 | 10 | // Equal reports whether two slices are equal: the same length and all 11 | // elements equal. If the lengths are different, Equal returns false. 12 | // Otherwise, the elements are compared in increasing index order, and the 13 | // comparison stops at the first unequal pair. 14 | // Floating point NaNs are not considered equal. 15 | func Equal[E comparable](s1, s2 []E) bool { 16 | return slices.Equal(s1, s2) 17 | } 18 | 19 | // EqualFunc reports whether two slices are equal using a comparison 20 | // function on each pair of elements. If the lengths are different, 21 | // EqualFunc returns false. Otherwise, the elements are compared in 22 | // increasing index order, and the comparison stops at the first index 23 | // for which eq returns false. 24 | func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool { 25 | return slices.EqualFunc(s1, s2, eq) 26 | } 27 | 28 | // Compare compares the elements of s1 and s2. 29 | // The elements are compared sequentially, starting at index 0, 30 | // until one element is not equal to the other. 31 | // The result of comparing the first non-matching elements is returned. 32 | // If both slices are equal until one of them ends, the shorter slice is 33 | // considered less than the longer one. 34 | // The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. 35 | // Comparisons involving floating point NaNs are ignored. 36 | func Compare[E constraints.Ordered](s1, s2 []E) int { 37 | return slices.Compare(s1, s2) 38 | } 39 | 40 | // CompareFunc is like Compare but uses a comparison function 41 | // on each pair of elements. The elements are compared in increasing 42 | // index order, and the comparisons stop after the first time cmp 43 | // returns non-zero. 44 | // The result is the first non-zero result of cmp; if cmp always 45 | // returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2), 46 | // and +1 if len(s1) > len(s2). 47 | func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { 48 | return slices.CompareFunc(s1, s2, cmp) 49 | } 50 | 51 | // Index returns the index of the first occurrence of v in s, 52 | // or -1 if not present. 53 | func Index[E comparable](s []E, v E) int { 54 | return slices.Index(s, v) 55 | } 56 | 57 | // IndexFunc returns the first index i satisfying f(s[i]), 58 | // or -1 if none do. 59 | func IndexFunc[E any](s []E, f func(E) bool) int { 60 | return slices.IndexFunc(s, f) 61 | } 62 | 63 | // Contains reports whether v is present in s. 64 | func Contains[E comparable](s []E, v E) bool { 65 | return slices.Contains(s, v) 66 | } 67 | 68 | // Insert inserts the values v... into s at index i, 69 | // returning the modified slice. 70 | // In the returned slice r, r[i] == v[0]. 71 | // Insert panics if i is out of range. 72 | // This function is O(len(s) + len(v)). 73 | func Insert[S ~[]E, E any](s S, i int, v ...E) S { 74 | return slices.Insert(s, i, v...) 75 | } 76 | 77 | // Delete removes the elements s[i:j] from s, returning the modified slice. 78 | // Delete panics if s[i:j] is not a valid slice of s. 79 | // Delete modifies the contents of the slice s; it does not create a new slice. 80 | // Delete is O(len(s)-(j-i)), so if many items must be deleted, it is better to 81 | // make a single call deleting them all together than to delete one at a time. 82 | func Delete[S ~[]E, E any](s S, i, j int) S { 83 | return slices.Delete(s, i, j) 84 | } 85 | 86 | // Clone returns a copy of the slice. 87 | // The elements are copied using assignment, so this is a shallow clone. 88 | func Clone[S ~[]E, E any](s S) S { 89 | return slices.Clone(s) 90 | } 91 | 92 | // Compact replaces consecutive runs of equal elements with a single copy. 93 | // This is like the uniq command found on Unix. 94 | // Compact modifies the contents of the slice s; it does not create a new slice. 95 | // Intended usage is to assign the result back to the input slice. 96 | func Compact[S ~[]E, E comparable](s S) S { 97 | return slices.Compact(s) 98 | } 99 | 100 | // CompactFunc is like Compact but uses a comparison function. 101 | func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { 102 | return slices.CompactFunc(s, eq) 103 | } 104 | 105 | // Grow increases the slice's capacity, if necessary, to guarantee space for 106 | // another n elements. After Grow(n), at least n elements can be appended 107 | // to the slice without another allocation. Grow may modify elements of the 108 | // slice between the length and the capacity. If n is negative or too large to 109 | // allocate the memory, Grow panics. 110 | func Grow[S ~[]E, E any](s S, n int) S { 111 | return slices.Grow(s, n) 112 | } 113 | 114 | // Clip removes unused capacity from the slice, returning s[:len(s):len(s)]. 115 | func Clip[S ~[]E, E any](s S) S { 116 | return slices.Clip(s) 117 | } 118 | -------------------------------------------------------------------------------- /slices/delegated_sort.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "golang.org/x/exp/constraints" 5 | "golang.org/x/exp/slices" 6 | ) 7 | 8 | // This file delegates to the official slices package, so that we end up with a superset of the official API. 9 | 10 | // Sort sorts a slice of any ordered type in ascending order. 11 | func Sort[E constraints.Ordered](x []E) { 12 | slices.Sort(x) 13 | } 14 | 15 | // Sort sorts the slice x in ascending order as determined by the less function. 16 | // This sort is not guaranteed to be stable. 17 | func SortFunc[E any](x []E, less func(a, b E) bool) { 18 | slices.SortFunc(x, less) 19 | } 20 | 21 | // SortStable sorts the slice x while keeping the original order of equal 22 | // elements, using less to compare elements. 23 | func SortStableFunc[E any](x []E, less func(a, b E) bool) { 24 | slices.SortStableFunc(x, less) 25 | } 26 | 27 | // IsSorted reports whether x is sorted in ascending order. 28 | func IsSorted[E constraints.Ordered](x []E) bool { 29 | return slices.IsSorted(x) 30 | } 31 | 32 | // IsSortedFunc reports whether x is sorted in ascending order, with less as the 33 | // comparison function. 34 | func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool { 35 | return slices.IsSortedFunc(x, less) 36 | } 37 | 38 | // BinarySearch searches for target in a sorted slice and returns the smallest 39 | // index at which target is found. If the target is not found, the index at 40 | // which it could be inserted into the slice is returned; therefore, if the 41 | // intention is to find target itself a separate check for equality with the 42 | // element at the returned index is required. 43 | func BinarySearch[E constraints.Ordered](x []E, target E) int { 44 | return slices.BinarySearch(x, target) 45 | } 46 | 47 | // BinarySearchFunc uses binary search to find and return the smallest index i 48 | // in [0, n) at which ok(i) is true, assuming that on the range [0, n), 49 | // ok(i) == true implies ok(i+1) == true. That is, BinarySearchFunc requires 50 | // that ok is false for some (possibly empty) prefix of the input range [0, n) 51 | // and then true for the (possibly empty) remainder; BinarySearchFunc returns 52 | // the first true index. If there is no such index, BinarySearchFunc returns n. 53 | // (Note that the "not found" return value is not -1 as in, for instance, 54 | // strings.Index.) Search calls ok(i) only for i in the range [0, n). 55 | func BinarySearchFunc[E any](x []E, ok func(E) bool) int { 56 | return slices.BinarySearchFunc(x, ok) 57 | } 58 | -------------------------------------------------------------------------------- /slices/slices.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "golang.org/x/exp/constraints" 5 | "golang.org/x/exp/slices" 6 | ) 7 | 8 | // This file contains the new functions that do not live in the official slices package. 9 | 10 | func Some[T any](slice []T, test func(T) bool) bool { 11 | for _, value := range slice { 12 | if test(value) { 13 | return true 14 | } 15 | } 16 | 17 | return false 18 | } 19 | 20 | func Every[T any](slice []T, test func(T) bool) bool { 21 | for _, value := range slice { 22 | if !test(value) { 23 | return false 24 | } 25 | } 26 | 27 | return true 28 | } 29 | 30 | // Produces a new slice, leaves the input slice untouched. 31 | func Map[T any, V any](slice []T, f func(T) V) []V { 32 | result := make([]V, 0, len(slice)) 33 | for _, value := range slice { 34 | result = append(result, f(value)) 35 | } 36 | 37 | return result 38 | } 39 | 40 | // Produces a new slice, leaves the input slice untouched. 41 | func MapWithIndex[T any, V any](slice []T, f func(T, int) V) []V { 42 | result := make([]V, 0, len(slice)) 43 | for i, value := range slice { 44 | result = append(result, f(value, i)) 45 | } 46 | 47 | return result 48 | } 49 | 50 | func TryMap[T any, V any](slice []T, f func(T) (V, error)) ([]V, error) { 51 | result := make([]V, 0, len(slice)) 52 | for _, value := range slice { 53 | output, err := f(value) 54 | if err != nil { 55 | return nil, err 56 | } 57 | result = append(result, output) 58 | } 59 | 60 | return result, nil 61 | } 62 | 63 | func TryMapWithIndex[T any, V any](slice []T, f func(T, int) (V, error)) ([]V, error) { 64 | result := make([]V, 0, len(slice)) 65 | for i, value := range slice { 66 | output, err := f(value, i) 67 | if err != nil { 68 | return nil, err 69 | } 70 | result = append(result, output) 71 | } 72 | 73 | return result, nil 74 | } 75 | 76 | // Produces a new slice, leaves the input slice untouched. 77 | func FlatMap[T any, V any](slice []T, f func(T) []V) []V { 78 | // impossible to know how long this slice will be in the end but the length 79 | // of the original slice is the lower bound 80 | result := make([]V, 0, len(slice)) 81 | for _, value := range slice { 82 | result = append(result, f(value)...) 83 | } 84 | 85 | return result 86 | } 87 | 88 | func FlatMapWithIndex[T any, V any](slice []T, f func(T, int) []V) []V { 89 | // impossible to know how long this slice will be in the end but the length 90 | // of the original slice is the lower bound 91 | result := make([]V, 0, len(slice)) 92 | for i, value := range slice { 93 | result = append(result, f(value, i)...) 94 | } 95 | 96 | return result 97 | } 98 | 99 | func Flatten[T any](slice [][]T) []T { 100 | result := make([]T, 0, len(slice)) 101 | for _, subSlice := range slice { 102 | result = append(result, subSlice...) 103 | } 104 | return result 105 | } 106 | 107 | func MapInPlace[T any](slice []T, f func(T) T) { 108 | for i, value := range slice { 109 | slice[i] = f(value) 110 | } 111 | } 112 | 113 | // Produces a new slice, leaves the input slice untouched. 114 | func Filter[T any](slice []T, test func(T) bool) []T { 115 | result := make([]T, 0, len(slice)) 116 | for _, element := range slice { 117 | if test(element) { 118 | result = append(result, element) 119 | } 120 | } 121 | return result 122 | } 123 | 124 | // Produces a new slice, leaves the input slice untouched. 125 | func FilterWithIndex[T any](slice []T, f func(T, int) bool) []T { 126 | result := make([]T, 0, len(slice)) 127 | for i, value := range slice { 128 | if f(value, i) { 129 | result = append(result, value) 130 | } 131 | } 132 | 133 | return result 134 | } 135 | 136 | func TryFilter[T any](slice []T, test func(T) (bool, error)) ([]T, error) { 137 | result := make([]T, 0, len(slice)) 138 | for _, element := range slice { 139 | ok, err := test(element) 140 | if err != nil { 141 | return nil, err 142 | } 143 | if ok { 144 | result = append(result, element) 145 | } 146 | } 147 | return result, nil 148 | } 149 | 150 | func TryFilterWithIndex[T any](slice []T, test func(T, int) (bool, error)) ([]T, error) { 151 | result := make([]T, 0, len(slice)) 152 | for i, element := range slice { 153 | ok, err := test(element, i) 154 | if err != nil { 155 | return nil, err 156 | } 157 | if ok { 158 | result = append(result, element) 159 | } 160 | } 161 | return result, nil 162 | } 163 | 164 | // Mutates original slice. Intended usage is to reassign the slice result to the input slice. 165 | func FilterInPlace[T any](slice []T, test func(T) bool) []T { 166 | newLength := 0 167 | for _, element := range slice { 168 | if test(element) { 169 | slice[newLength] = element 170 | newLength++ 171 | } 172 | } 173 | 174 | return slice[:newLength] 175 | } 176 | 177 | // Produces a new slice, leaves the input slice untouched 178 | func Reverse[T any](slice []T) []T { 179 | result := make([]T, len(slice)) 180 | for i := range slice { 181 | result[i] = slice[len(slice)-1-i] 182 | } 183 | return result 184 | } 185 | 186 | func ReverseInPlace[T any](slice []T) { 187 | for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { 188 | slice[i], slice[j] = slice[j], slice[i] 189 | } 190 | } 191 | 192 | // Produces a new slice, leaves the input slice untouched. 193 | func FilterMap[T any, E any](slice []T, test func(T) (E, bool)) []E { 194 | result := make([]E, 0, len(slice)) 195 | for _, element := range slice { 196 | mapped, ok := test(element) 197 | if ok { 198 | result = append(result, mapped) 199 | } 200 | } 201 | 202 | return result 203 | } 204 | 205 | func FilterMapWithIndex[T any, E any](slice []T, test func(T, int) (E, bool)) []E { 206 | result := make([]E, 0, len(slice)) 207 | for i, element := range slice { 208 | mapped, ok := test(element, i) 209 | if ok { 210 | result = append(result, mapped) 211 | } 212 | } 213 | 214 | return result 215 | } 216 | 217 | func TryFilterMap[T any, E any](slice []T, test func(T) (E, bool, error)) ([]E, error) { 218 | result := make([]E, 0, len(slice)) 219 | for _, element := range slice { 220 | mapped, ok, err := test(element) 221 | if err != nil { 222 | return nil, err 223 | } 224 | if ok { 225 | result = append(result, mapped) 226 | } 227 | } 228 | 229 | return result, nil 230 | } 231 | 232 | func TryFilterMapWithIndex[T any, E any](slice []T, test func(T, int) (E, bool, error)) ([]E, error) { 233 | result := make([]E, 0, len(slice)) 234 | for i, element := range slice { 235 | mapped, ok, err := test(element, i) 236 | if err != nil { 237 | return nil, err 238 | } 239 | if ok { 240 | result = append(result, mapped) 241 | } 242 | } 243 | 244 | return result, nil 245 | } 246 | 247 | // Prepends items to the beginning of a slice. 248 | // E.g. Prepend([]int{1,2}, 3, 4) = []int{3,4,1,2} 249 | // Mutates original slice. Intended usage is to reassign the slice result to the input slice. 250 | func Prepend[T any](slice []T, values ...T) []T { 251 | return append(values, slice...) 252 | } 253 | 254 | // Removes the element at the given index. Intended usage is to reassign the result to the input slice. 255 | func Remove[T any](slice []T, index int) []T { 256 | return slices.Delete(slice, index, index+1) 257 | } 258 | 259 | // Removes the element at the 'fromIndex' and then inserts it at 'toIndex'. 260 | // Operates on the input slice. Expected use is to reassign the result to the input slice. 261 | func Move[T any](slice []T, fromIndex int, toIndex int) []T { 262 | item := slice[fromIndex] 263 | slice = Remove(slice, fromIndex) 264 | return slices.Insert(slice, toIndex, item) 265 | } 266 | 267 | // Swaps two elements at the given indices. 268 | // Operates on the input slice. 269 | func Swap[T any](slice []T, index1 int, index2 int) { 270 | slice[index1], slice[index2] = slice[index2], slice[index1] 271 | } 272 | 273 | // Similar to Append but we leave the original slice untouched and return a new slice 274 | func Concat[T any](slice []T, values ...T) []T { 275 | newSlice := make([]T, 0, len(slice)+len(values)) 276 | newSlice = append(newSlice, slice...) 277 | newSlice = append(newSlice, values...) 278 | return newSlice 279 | } 280 | 281 | func ContainsFunc[T any](slice []T, f func(T) bool) bool { 282 | return IndexFunc(slice, f) != -1 283 | } 284 | 285 | // Pops item from the end of the slice and returns it, along with the updated slice 286 | // Mutates original slice. Intended usage is to reassign the slice result to the input slice. 287 | func Pop[T any](slice []T) (T, []T) { 288 | index := len(slice) - 1 289 | value := slice[index] 290 | slice = slice[0:index] 291 | return value, slice 292 | } 293 | 294 | // Shifts item from the beginning of the slice and returns it, along with the updated slice. 295 | // Mutates original slice. Intended usage is to reassign the slice result to the input slice. 296 | func Shift[T any](slice []T) (T, []T) { 297 | value := slice[0] 298 | slice = slice[1:] 299 | return value, slice 300 | } 301 | 302 | func Partition[T any](slice []T, test func(T) bool) ([]T, []T) { 303 | left := make([]T, 0, len(slice)) 304 | right := make([]T, 0, len(slice)) 305 | 306 | for _, value := range slice { 307 | if test(value) { 308 | left = append(left, value) 309 | } else { 310 | right = append(right, value) 311 | } 312 | } 313 | 314 | return left, right 315 | } 316 | 317 | func MaxBy[T any, V constraints.Ordered](slice []T, f func(T) V) V { 318 | if len(slice) == 0 { 319 | return zero[V]() 320 | } 321 | 322 | max := f(slice[0]) 323 | for _, element := range slice[1:] { 324 | value := f(element) 325 | if value > max { 326 | max = value 327 | } 328 | } 329 | return max 330 | } 331 | 332 | func MinBy[T any, V constraints.Ordered](slice []T, f func(T) V) V { 333 | if len(slice) == 0 { 334 | return zero[V]() 335 | } 336 | 337 | min := f(slice[0]) 338 | for _, element := range slice[1:] { 339 | value := f(element) 340 | if value < min { 341 | min = value 342 | } 343 | } 344 | return min 345 | } 346 | 347 | func Find[T any](slice []T, f func(T) bool) (T, bool) { 348 | for _, element := range slice { 349 | if f(element) { 350 | return element, true 351 | } 352 | } 353 | return zero[T](), false 354 | } 355 | 356 | // Sometimes you need to find an element and then map it to some other value based on 357 | // information you obtained while finding it. This function lets you do that 358 | func FindMap[T any, V any](slice []T, f func(T) (V, bool)) (V, bool) { 359 | for _, element := range slice { 360 | if value, ok := f(element); ok { 361 | return value, true 362 | } 363 | } 364 | return zero[V](), false 365 | } 366 | 367 | func ForEach[T any](slice []T, f func(T)) { 368 | for _, element := range slice { 369 | f(element) 370 | } 371 | } 372 | 373 | func ForEachWithIndex[T any](slice []T, f func(T, int)) { 374 | for i, element := range slice { 375 | f(element, i) 376 | } 377 | } 378 | 379 | func TryForEach[T any](slice []T, f func(T) error) error { 380 | for _, element := range slice { 381 | if err := f(element); err != nil { 382 | return err 383 | } 384 | } 385 | return nil 386 | } 387 | 388 | func TryForEachWithIndex[T any](slice []T, f func(T, int) error) error { 389 | for i, element := range slice { 390 | if err := f(element, i); err != nil { 391 | return err 392 | } 393 | } 394 | return nil 395 | } 396 | 397 | func Sum[T constraints.Ordered](i []T) T { 398 | sum := zero[T]() 399 | for _, value := range i { 400 | sum += value 401 | } 402 | return sum 403 | } 404 | 405 | func zero[T any]() T { 406 | var value T 407 | return value 408 | } 409 | -------------------------------------------------------------------------------- /slices/slices_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/jesseduffield/generics/internal/testutils" 8 | "golang.org/x/exp/slices" 9 | ) 10 | 11 | func TestInsert(t *testing.T) { 12 | tests := []struct { 13 | startSlice []int 14 | index int 15 | values []int 16 | endSlice []int 17 | }{ 18 | {[]int{}, 0, []int{1}, []int{1}}, 19 | {[]int{1}, 0, []int{2}, []int{2, 1}}, 20 | {[]int{1, 2}, 1, []int{3}, []int{1, 3, 2}}, 21 | {[]int{1, 2}, 2, []int{3}, []int{1, 2, 3}}, 22 | {[]int{1, 2}, 2, []int{3, 4}, []int{1, 2, 3, 4}}, 23 | {[]int{1, 2}, 1, []int{3, 4}, []int{1, 3, 4, 2}}, 24 | } 25 | for _, test := range tests { 26 | result := Insert(test.startSlice, test.index, test.values...) 27 | testutils.ExpectSlice(t, test.endSlice, result) 28 | } 29 | 30 | panicTests := []struct { 31 | startSlice []int 32 | index int 33 | value int 34 | }{ 35 | {[]int{}, 1, 1}, 36 | {[]int{}, 2, 1}, 37 | {[]int{1}, 2, 1}, 38 | {[]int{1}, -1, 1}, 39 | } 40 | for _, test := range panicTests { 41 | func() { 42 | defer testutils.ExpectPanic(t) 43 | Insert(test.startSlice, test.index, test.value) 44 | }() 45 | } 46 | } 47 | 48 | func TestRemove(t *testing.T) { 49 | tests := []struct { 50 | startSlice []int 51 | index int 52 | endSlice []int 53 | }{ 54 | {[]int{1}, 0, []int{}}, 55 | {[]int{1, 2}, 0, []int{2}}, 56 | {[]int{1, 2}, 1, []int{1}}, 57 | } 58 | for _, test := range tests { 59 | result := Remove(test.startSlice, test.index) 60 | testutils.ExpectSlice(t, test.endSlice, result) 61 | } 62 | 63 | panicTests := []struct { 64 | startSlice []int 65 | index int 66 | }{ 67 | {[]int{}, 0}, 68 | {[]int{}, 1}, 69 | {[]int{1}, 1}, 70 | {[]int{1}, -1}, 71 | } 72 | for _, test := range panicTests { 73 | func() { 74 | defer testutils.ExpectPanic(t) 75 | Remove(test.startSlice, test.index) 76 | }() 77 | } 78 | } 79 | 80 | func TestPop(t *testing.T) { 81 | tests := []struct { 82 | startSlice []int 83 | value int 84 | endSlice []int 85 | }{ 86 | {[]int{1}, 1, []int{}}, 87 | {[]int{1, 2}, 2, []int{1}}, 88 | {[]int{1, 2, 3}, 3, []int{1, 2}}, 89 | } 90 | for _, test := range tests { 91 | value, slice := Pop(test.startSlice) 92 | if value != test.value { 93 | t.Errorf("expected %d, got %d", test.value, value) 94 | } 95 | testutils.ExpectSlice(t, test.endSlice, slice) 96 | } 97 | 98 | panicTests := []struct { 99 | startSlice []int 100 | }{ 101 | {[]int{}}, 102 | } 103 | for _, test := range panicTests { 104 | func() { 105 | defer testutils.ExpectPanic(t) 106 | Pop(test.startSlice) 107 | }() 108 | } 109 | } 110 | 111 | func TestFilter(t *testing.T) { 112 | even := func(value int) bool { return value%2 == 0 } 113 | tests := []struct { 114 | startSlice []int 115 | testFunc func(value int) bool 116 | endSlice []int 117 | }{ 118 | {[]int{1}, even, []int{}}, 119 | {[]int{1, 2}, even, []int{2}}, 120 | {[]int{1, 2, 3}, even, []int{2}}, 121 | {[]int{1, 2, 3, 4}, even, []int{2, 4}}, 122 | } 123 | for _, test := range tests { 124 | testSlice := slices.Clone(test.startSlice) 125 | testutils.ExpectSlice(t, test.endSlice, Filter(testSlice, test.testFunc)) 126 | testutils.ExpectSlice(t, testSlice, test.startSlice) 127 | } 128 | } 129 | 130 | func TestFilterInPlace(t *testing.T) { 131 | even := func(value int) bool { return value%2 == 0 } 132 | tests := []struct { 133 | startSlice []int 134 | testFunc func(value int) bool 135 | endSlice []int 136 | }{ 137 | {[]int{1}, even, []int{}}, 138 | {[]int{1, 2}, even, []int{2}}, 139 | {[]int{1, 2, 3}, even, []int{2}}, 140 | {[]int{1, 2, 3, 4}, even, []int{2, 4}}, 141 | } 142 | for _, test := range tests { 143 | result := FilterInPlace(test.startSlice, test.testFunc) 144 | testutils.ExpectSlice(t, test.endSlice, result) 145 | } 146 | } 147 | 148 | func TestFilterMap(t *testing.T) { 149 | slice := []int{1, 2, 3, 4} 150 | result := FilterMap(slice, 151 | func(value int) (string, bool) { 152 | if value%2 != 0 { 153 | return "", false 154 | } 155 | 156 | return strconv.Itoa(value * 2), true 157 | }, 158 | ) 159 | 160 | testutils.ExpectSlice(t, []string{"4", "8"}, result) 161 | } 162 | 163 | func TestMap(t *testing.T) { 164 | double := func(value int) int { return value * 2 } 165 | tests := []struct { 166 | startSlice []int 167 | mapFunc func(value int) int 168 | endSlice []int 169 | }{ 170 | {[]int{}, double, []int{}}, 171 | {[]int{1}, double, []int{2}}, 172 | {[]int{1, 2}, double, []int{2, 4}}, 173 | {[]int{1, 2, 3}, double, []int{2, 4, 6}}, 174 | {[]int{1, 2, 3, 4}, double, []int{2, 4, 6, 8}}, 175 | } 176 | 177 | for _, test := range tests { 178 | testSlice := slices.Clone(test.startSlice) 179 | testutils.ExpectSlice(t, test.endSlice, Map(testSlice, test.mapFunc)) 180 | testutils.ExpectSlice(t, test.startSlice, testSlice) 181 | } 182 | } 183 | 184 | func TestMapWithIndex(t *testing.T) { 185 | double := func(value int, i int) int { return value*2 + i } 186 | tests := []struct { 187 | startSlice []int 188 | mapFunc func(value int, i int) int 189 | endSlice []int 190 | }{ 191 | {[]int{}, double, []int{}}, 192 | {[]int{1}, double, []int{2}}, 193 | {[]int{1, 2}, double, []int{2, 5}}, 194 | {[]int{1, 2, 3}, double, []int{2, 5, 8}}, 195 | {[]int{1, 2, 3, 4}, double, []int{2, 5, 8, 11}}, 196 | } 197 | 198 | for _, test := range tests { 199 | testSlice := slices.Clone(test.startSlice) 200 | testutils.ExpectSlice(t, test.endSlice, MapWithIndex(testSlice, test.mapFunc)) 201 | testutils.ExpectSlice(t, test.startSlice, testSlice) 202 | } 203 | } 204 | 205 | func TestFilterWithIndex(t *testing.T) { 206 | double := func(value int, i int) bool { return (value*2+i)%2 == 0 } 207 | tests := []struct { 208 | startSlice []int 209 | mapFunc func(value int, i int) bool 210 | endSlice []int 211 | }{ 212 | {[]int{}, double, []int{}}, 213 | {[]int{1}, double, []int{1}}, 214 | {[]int{1, 2}, double, []int{1}}, 215 | {[]int{1, 2, 3}, double, []int{1, 3}}, 216 | {[]int{1, 2, 3, 4}, double, []int{1, 3}}, 217 | } 218 | 219 | for _, test := range tests { 220 | testSlice := slices.Clone(test.startSlice) 221 | testutils.ExpectSlice(t, test.endSlice, FilterWithIndex(testSlice, test.mapFunc)) 222 | testutils.ExpectSlice(t, test.startSlice, testSlice) 223 | } 224 | } 225 | 226 | func TestFlatMap(t *testing.T) { 227 | f := func(value int) []int { return []int{value * 2, value * 4} } 228 | tests := []struct { 229 | startSlice []int 230 | mapFunc func(value int) []int 231 | endSlice []int 232 | }{ 233 | {[]int{}, f, []int{}}, 234 | {[]int{1}, f, []int{2, 4}}, 235 | {[]int{1, 2}, f, []int{2, 4, 4, 8}}, 236 | {[]int{1, 2, 3}, f, []int{2, 4, 4, 8, 6, 12}}, 237 | {[]int{1, 2, 3, 4}, f, []int{2, 4, 4, 8, 6, 12, 8, 16}}, 238 | } 239 | 240 | for _, test := range tests { 241 | testSlice := slices.Clone(test.startSlice) 242 | testutils.ExpectSlice(t, test.endSlice, FlatMap(testSlice, test.mapFunc)) 243 | testutils.ExpectSlice(t, test.startSlice, testSlice) 244 | } 245 | } 246 | 247 | func TestFlatten(t *testing.T) { 248 | tests := []struct { 249 | startSlice [][]int 250 | endSlice []int 251 | }{ 252 | {[][]int{}, []int{}}, 253 | {[][]int{{1}}, []int{1}}, 254 | {[][]int{{1, 2}, {3}}, []int{1, 2, 3}}, 255 | {[][]int{{1, 2}, {}, {3, 4}}, []int{1, 2, 3, 4}}, 256 | } 257 | 258 | for _, test := range tests { 259 | testutils.ExpectSlice(t, test.endSlice, Flatten(test.startSlice)) 260 | } 261 | } 262 | 263 | func TestMapInPlace(t *testing.T) { 264 | double := func(value int) int { return value * 2 } 265 | tests := []struct { 266 | startSlice []int 267 | mapFunc func(value int) int 268 | endSlice []int 269 | }{ 270 | {[]int{}, double, []int{}}, 271 | {[]int{1}, double, []int{2}}, 272 | {[]int{1, 2}, double, []int{2, 4}}, 273 | {[]int{1, 2, 3}, double, []int{2, 4, 6}}, 274 | {[]int{1, 2, 3, 4}, double, []int{2, 4, 6, 8}}, 275 | } 276 | for _, test := range tests { 277 | testSlice := slices.Clone(test.startSlice) 278 | MapInPlace(testSlice, test.mapFunc) 279 | testutils.ExpectSlice(t, test.endSlice, testSlice) 280 | } 281 | } 282 | 283 | func TestPrepend(t *testing.T) { 284 | tests := []struct { 285 | slice []int 286 | values []int 287 | expectedSlice []int 288 | }{ 289 | {[]int{}, []int{}, []int{}}, 290 | {[]int{}, []int{1}, []int{1}}, 291 | {[]int{1}, []int{2}, []int{2, 1}}, 292 | {[]int{1, 2}, []int{3}, []int{3, 1, 2}}, 293 | {[]int{1, 2}, []int{3, 4}, []int{3, 4, 1, 2}}, 294 | } 295 | for _, test := range tests { 296 | slice := Prepend(test.slice, test.values...) 297 | testutils.ExpectSlice(t, test.expectedSlice, slice) 298 | } 299 | } 300 | 301 | func TestConcat(t *testing.T) { 302 | tests := []struct { 303 | startSlice []int 304 | values []int 305 | endSlice []int 306 | }{ 307 | {[]int{}, []int{}, []int{}}, 308 | {[]int{1}, []int{2}, []int{1, 2}}, 309 | {[]int{1, 2}, []int{3, 4}, []int{1, 2, 3, 4}}, 310 | } 311 | for _, test := range tests { 312 | testSlice := slices.Clone(test.startSlice) 313 | result := Concat(testSlice, test.values...) 314 | testutils.ExpectSlice(t, test.endSlice, result) 315 | testutils.ExpectSlice(t, test.startSlice, testSlice) 316 | } 317 | } 318 | 319 | func TestSome(t *testing.T) { 320 | even := func(value int) bool { return value%2 == 0 } 321 | tests := []struct { 322 | startSlice []int 323 | testFunc func(value int) bool 324 | expected bool 325 | }{ 326 | {[]int{}, even, false}, 327 | {[]int{1}, even, false}, 328 | {[]int{2}, even, true}, 329 | {[]int{1, 2}, even, true}, 330 | } 331 | for _, test := range tests { 332 | if Some(test.startSlice, test.testFunc) != test.expected { 333 | t.Errorf("Some(%v) = %v, expected %v", 334 | test.startSlice, Some(test.startSlice, test.testFunc), test.expected, 335 | ) 336 | } 337 | } 338 | } 339 | 340 | func TestEvery(t *testing.T) { 341 | even := func(value int) bool { return value%2 == 0 } 342 | tests := []struct { 343 | startSlice []int 344 | testFunc func(value int) bool 345 | expected bool 346 | }{ 347 | {[]int{}, even, true}, 348 | {[]int{1}, even, false}, 349 | {[]int{2}, even, true}, 350 | {[]int{1, 2}, even, false}, 351 | {[]int{2, 2}, even, true}, 352 | } 353 | for _, test := range tests { 354 | if Every(test.startSlice, test.testFunc) != test.expected { 355 | t.Errorf("Every(%v) = %v, expected %v", 356 | test.startSlice, Every(test.startSlice, test.testFunc), test.expected, 357 | ) 358 | } 359 | } 360 | } 361 | 362 | func TestReverse(t *testing.T) { 363 | tests := []struct { 364 | startSlice []int 365 | expected []int 366 | }{ 367 | {[]int{}, []int{}}, 368 | {[]int{1}, []int{1}}, 369 | {[]int{1, 2}, []int{2, 1}}, 370 | {[]int{1, 2, 3}, []int{3, 2, 1}}, 371 | } 372 | for _, test := range tests { 373 | testSlice := slices.Clone(test.startSlice) 374 | result := Reverse(testSlice) 375 | testutils.ExpectSlice(t, test.expected, result) 376 | testutils.ExpectSlice(t, test.startSlice, testSlice) 377 | } 378 | } 379 | 380 | func TestReverseInPlace(t *testing.T) { 381 | tests := []struct { 382 | startSlice []int 383 | expected []int 384 | }{ 385 | {[]int{}, []int{}}, 386 | {[]int{1}, []int{1}}, 387 | {[]int{1, 2}, []int{2, 1}}, 388 | {[]int{1, 2, 3}, []int{3, 2, 1}}, 389 | } 390 | for _, test := range tests { 391 | testSlice := slices.Clone(test.startSlice) 392 | ReverseInPlace(testSlice) 393 | testutils.ExpectSlice(t, test.expected, testSlice) 394 | } 395 | } 396 | 397 | func TestClone(t *testing.T) { 398 | slice := []int{1, 2, 3} 399 | clone := Clone(slice) 400 | testutils.ExpectSlice(t, []int{1, 2, 3}, clone) 401 | 402 | // ensure that the clone has its own slice 403 | clone = Prepend(clone, 0) 404 | testutils.ExpectSlice(t, []int{0, 1, 2, 3}, clone) 405 | testutils.ExpectSlice(t, []int{1, 2, 3}, slice) 406 | } 407 | 408 | func TestMove(t *testing.T) { 409 | tests := []struct { 410 | startSlice []int 411 | from int 412 | to int 413 | endSlice []int 414 | }{ 415 | { 416 | startSlice: []int{1, 2, 3}, 417 | from: 0, 418 | to: 1, 419 | endSlice: []int{2, 1, 3}, 420 | }, 421 | { 422 | startSlice: []int{1, 2, 3, 4}, 423 | from: 2, 424 | to: 1, 425 | endSlice: []int{1, 3, 2, 4}, 426 | }, 427 | { 428 | startSlice: []int{1, 2, 3}, 429 | from: 1, 430 | to: 0, 431 | endSlice: []int{2, 1, 3}, 432 | }, 433 | { 434 | startSlice: []int{1, 2, 3, 4}, 435 | from: 0, 436 | to: 3, 437 | endSlice: []int{2, 3, 4, 1}, 438 | }, 439 | } 440 | 441 | for _, test := range tests { 442 | result := Move(test.startSlice, test.from, test.to) 443 | testutils.ExpectSlice(t, test.endSlice, result) 444 | } 445 | } 446 | 447 | func TestSwap(t *testing.T) { 448 | tests := []struct { 449 | startSlice []int 450 | from int 451 | to int 452 | endSlice []int 453 | }{ 454 | { 455 | startSlice: []int{1, 2, 3}, 456 | from: 0, 457 | to: 1, 458 | endSlice: []int{2, 1, 3}, 459 | }, 460 | { 461 | startSlice: []int{1, 2, 3, 4}, 462 | from: 2, 463 | to: 1, 464 | endSlice: []int{1, 3, 2, 4}, 465 | }, 466 | { 467 | startSlice: []int{1, 2, 3}, 468 | from: 1, 469 | to: 0, 470 | endSlice: []int{2, 1, 3}, 471 | }, 472 | { 473 | startSlice: []int{1, 2, 3, 4}, 474 | from: 0, 475 | to: 3, 476 | endSlice: []int{4, 2, 3, 1}, 477 | }, 478 | { 479 | startSlice: []int{1, 2, 3, 4}, 480 | from: 3, 481 | to: 0, 482 | endSlice: []int{4, 2, 3, 1}, 483 | }, 484 | } 485 | 486 | for _, test := range tests { 487 | Swap(test.startSlice, test.from, test.to) 488 | testutils.ExpectSlice(t, test.endSlice, test.startSlice) 489 | } 490 | } 491 | 492 | func TestEqual(t *testing.T) { 493 | tests := []struct { 494 | first []int 495 | second []int 496 | expected bool 497 | }{ 498 | {[]int{}, []int{}, true}, 499 | {[]int{1}, []int{1}, true}, 500 | {[]int{}, []int{1}, false}, 501 | {[]int{1, 2}, []int{1, 2}, true}, 502 | {[]int{1, 2}, []int{2, 1}, false}, 503 | {[]int{1, 2, 3}, []int{1, 2}, false}, 504 | } 505 | for _, test := range tests { 506 | if Equal(test.first, test.second) != test.expected { 507 | t.Errorf("Equal(%v, %v) = %v, expected %v", 508 | test.first, test.second, Equal(test.first, test.second), test.expected, 509 | ) 510 | } 511 | } 512 | } 513 | 514 | func TestCompact(t *testing.T) { 515 | tests := []struct { 516 | slice []int 517 | expected []int 518 | }{ 519 | {[]int{}, []int{}}, 520 | {[]int{1}, []int{1}}, 521 | {[]int{1, 2}, []int{1, 2}}, 522 | {[]int{1, 1, 2}, []int{1, 2}}, 523 | {[]int{1, 2, 1}, []int{1, 2, 1}}, 524 | {[]int{1, 1, 1}, []int{1}}, 525 | } 526 | for _, test := range tests { 527 | result := Compact(test.slice) 528 | testutils.ExpectSlice(t, test.expected, result) 529 | } 530 | } 531 | 532 | func TestIndex(t *testing.T) { 533 | tests := []struct { 534 | slice []int 535 | value int 536 | expected int 537 | }{ 538 | {[]int{}, 1, -1}, 539 | {[]int{1}, 1, 0}, 540 | {[]int{1, 1}, 1, 0}, 541 | {[]int{1, 1}, 2, -1}, 542 | {[]int{1, 2, 3}, 2, 1}, 543 | {[]int{1, 2, 3}, 3, 2}, 544 | } 545 | for _, test := range tests { 546 | if Index(test.slice, test.value) != test.expected { 547 | t.Errorf("Index(%v, %v) = %v, expected %v", test.slice, test.value, Index(test.slice, test.value), test.expected) 548 | } 549 | } 550 | } 551 | 552 | func TestIndexFunc(t *testing.T) { 553 | tests := []struct { 554 | slice []int 555 | f func(value int) bool 556 | expected int 557 | }{ 558 | {[]int{}, func(value int) bool { return true }, -1}, 559 | {[]int{1}, func(value int) bool { return true }, 0}, 560 | {[]int{1, 1}, func(value int) bool { return true }, 0}, 561 | {[]int{1, 1}, func(value int) bool { return false }, -1}, 562 | {[]int{1, 2, 3}, func(value int) bool { return value == 2 }, 1}, 563 | {[]int{1, 2, 3}, func(value int) bool { return value == 3 }, 2}, 564 | } 565 | for _, test := range tests { 566 | if IndexFunc(test.slice, test.f) != test.expected { 567 | t.Errorf("IndexFunc(%v, func) = %v, expected %v", test.slice, IndexFunc(test.slice, test.f), test.expected) 568 | } 569 | } 570 | } 571 | 572 | func TestContains(t *testing.T) { 573 | tests := []struct { 574 | slice []int 575 | value int 576 | expected bool 577 | }{ 578 | {[]int{}, 1, false}, 579 | {[]int{1}, 1, true}, 580 | {[]int{1, 1}, 1, true}, 581 | {[]int{1, 1}, 2, false}, 582 | {[]int{1, 2, 3}, 2, true}, 583 | {[]int{1, 2, 3}, 3, true}, 584 | } 585 | for _, test := range tests { 586 | if Contains(test.slice, test.value) != test.expected { 587 | t.Errorf("Contains(%v, %v) = %v, expected %v", test.slice, test.value, Contains(test.slice, test.value), test.expected) 588 | } 589 | } 590 | } 591 | 592 | func TestContainsFunc(t *testing.T) { 593 | tests := []struct { 594 | slice []int 595 | f func(value int) bool 596 | expected bool 597 | }{ 598 | {[]int{}, func(value int) bool { return true }, false}, 599 | {[]int{1}, func(value int) bool { return true }, true}, 600 | {[]int{1, 1}, func(value int) bool { return true }, true}, 601 | {[]int{1, 1}, func(value int) bool { return false }, false}, 602 | {[]int{1, 2, 3}, func(value int) bool { return value == 2 }, true}, 603 | {[]int{1, 2, 3}, func(value int) bool { return value == 3 }, true}, 604 | } 605 | for _, test := range tests { 606 | if ContainsFunc(test.slice, test.f) != test.expected { 607 | t.Errorf("ContainsFunc(%v, func) = %v, expected %v", test.slice, ContainsFunc(test.slice, test.f), test.expected) 608 | } 609 | } 610 | } 611 | 612 | func TestShift(t *testing.T) { 613 | tests := []struct { 614 | slice []int 615 | expectedValue int 616 | expectedSlice []int 617 | }{ 618 | {[]int{1}, 1, []int{}}, 619 | {[]int{1, 2}, 1, []int{2}}, 620 | } 621 | for _, test := range tests { 622 | value, slice := Shift(test.slice) 623 | if value != test.expectedValue { 624 | t.Errorf("Shift(%v) = %v, expected %v", test.slice, value, test.expectedValue) 625 | } 626 | testutils.ExpectSlice(t, test.expectedSlice, slice) 627 | } 628 | } 629 | 630 | func TestPartition(t *testing.T) { 631 | even := func(value int) bool { return value%2 == 0 } 632 | tests := []struct { 633 | startSlice []int 634 | testFunc func(value int) bool 635 | endSliceLeft []int 636 | endSliceRight []int 637 | }{ 638 | {[]int{1}, even, []int{}, []int{1}}, 639 | {[]int{1, 2}, even, []int{2}, []int{1}}, 640 | {[]int{1, 2, 3}, even, []int{2}, []int{1, 3}}, 641 | {[]int{1, 2, 3, 4}, even, []int{2, 4}, []int{1, 3}}, 642 | } 643 | for _, test := range tests { 644 | testSlice := slices.Clone(test.startSlice) 645 | left, right := Partition(testSlice, test.testFunc) 646 | testutils.ExpectSlice(t, test.endSliceLeft, left) 647 | testutils.ExpectSlice(t, test.endSliceRight, right) 648 | testutils.ExpectSlice(t, testSlice, test.startSlice) 649 | } 650 | } 651 | 652 | func TestMaxBy(t *testing.T) { 653 | tests := []struct { 654 | slice []int 655 | f func(value int) int 656 | expected int 657 | }{ 658 | {[]int{}, func(value int) int { return value }, 0}, 659 | {[]int{1}, func(value int) int { return value }, 1}, 660 | {[]int{-1}, func(value int) int { return value }, -1}, 661 | {[]int{1, 2}, func(value int) int { return value }, 2}, 662 | {[]int{3, 1, 2, 3}, func(value int) int { return value }, 3}, 663 | {[]int{1, 2, 2, 3, 4}, func(value int) int { return value }, 4}, 664 | } 665 | for _, test := range tests { 666 | if MaxBy(test.slice, test.f) != test.expected { 667 | t.Errorf("MaxBy(%v, func) = %v, expected %v", test.slice, MaxBy(test.slice, test.f), test.expected) 668 | } 669 | } 670 | } 671 | 672 | func TestMinBy(t *testing.T) { 673 | tests := []struct { 674 | slice []int 675 | f func(value int) int 676 | expected int 677 | }{ 678 | {[]int{}, func(value int) int { return value }, 0}, 679 | {[]int{1}, func(value int) int { return value }, 1}, 680 | {[]int{-1}, func(value int) int { return value }, -1}, 681 | {[]int{3, 1, 2}, func(value int) int { return value }, 1}, 682 | {[]int{1, 2, 3}, func(value int) int { return value }, 1}, 683 | {[]int{1, 2, 3, 4}, func(value int) int { return value }, 1}, 684 | } 685 | for _, test := range tests { 686 | if MinBy(test.slice, test.f) != test.expected { 687 | t.Errorf("MinBy(%v, func) = %v, expected %v", test.slice, MinBy(test.slice, test.f), test.expected) 688 | } 689 | } 690 | } 691 | 692 | func TestSumInt(t *testing.T) { 693 | tests := []struct { 694 | slice []int 695 | expected int 696 | }{ 697 | {[]int{}, 0}, 698 | {[]int{1}, 1}, 699 | {[]int{1, 2}, 3}, 700 | {[]int{1, 2, 3}, 6}, 701 | } 702 | for _, test := range tests { 703 | if Sum(test.slice) != test.expected { 704 | t.Errorf("Sum(%v) = %v, expected %v", test.slice, Sum(test.slice), test.expected) 705 | } 706 | } 707 | } 708 | 709 | func TestSumString(t *testing.T) { 710 | tests := []struct { 711 | slice []string 712 | expected string 713 | }{ 714 | {[]string{}, ""}, 715 | {[]string{"a"}, "a"}, 716 | {[]string{"a", "b"}, "ab"}, 717 | } 718 | for _, test := range tests { 719 | if Sum(test.slice) != test.expected { 720 | t.Errorf("Sum(%v) = %v, expected %v", test.slice, Sum(test.slice), test.expected) 721 | } 722 | } 723 | } 724 | --------------------------------------------------------------------------------