Skip to content

Commit

Permalink
Generics migration (#237)
Browse files Browse the repository at this point in the history
* Generics migration

This attempts to migrate this library in the least invasive way by preserving as
much of the original API as possible. It does not change the tests in a
meaningful way nor does it attempt to upgrade any logic that can be simplified
or improved with generics. This is purely an API migration, and still requires a
lot of additional work to be fully ready.

* Fix a few broken tests around serialization

* Add v2 suffix

* Temporarily change mod name for testing

* Rename module to /v2
  • Loading branch information
PapaCharlie committed Jan 7, 2024
1 parent 10d6c5b commit 14f7142
Show file tree
Hide file tree
Showing 135 changed files with 3,249 additions and 4,078 deletions.
25 changes: 20 additions & 5 deletions containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,39 @@
// Serialization provides serializers (marshalers) and deserializers (unmarshalers).
package containers

import "github.com/emirpasic/gods/utils"
import (
"cmp"
"slices"

"github.com/emirpasic/gods/v2/utils"
)

// Container is base interface that all data structures implement.
type Container interface {
type Container[T any] interface {
Empty() bool
Size() int
Clear()
Values() []interface{}
Values() []T
String() string
}

// GetSortedValues returns sorted container's elements with respect to the passed comparator.
// Does not affect the ordering of elements within the container.
func GetSortedValues(container Container, comparator utils.Comparator) []interface{} {
func GetSortedValues[T cmp.Ordered](container Container[T]) []T {
values := container.Values()
if len(values) < 2 {
return values
}
slices.Sort(values)
return values
}

// GetSortedValuesFunc is the equivalent of GetSortedValues for containers of values that are not ordered.
func GetSortedValuesFunc[T any](container Container[T], comparator utils.Comparator[T]) []T {
values := container.Values()
if len(values) < 2 {
return values
}
utils.Sort(values, comparator)
slices.SortFunc(values, comparator)
return values
}
48 changes: 28 additions & 20 deletions containers/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,34 @@
package containers

import (
"cmp"
"fmt"
"github.com/emirpasic/gods/utils"
"strings"
"testing"
)

// For testing purposes
type ContainerTest struct {
values []interface{}
type ContainerTest[T any] struct {
values []T
}

func (container ContainerTest) Empty() bool {
func (container ContainerTest[T]) Empty() bool {
return len(container.values) == 0
}

func (container ContainerTest) Size() int {
func (container ContainerTest[T]) Size() int {
return len(container.values)
}

func (container ContainerTest) Clear() {
container.values = []interface{}{}
func (container ContainerTest[T]) Clear() {
container.values = []T{}
}

func (container ContainerTest) Values() []interface{} {
func (container ContainerTest[T]) Values() []T {
return container.values
}

func (container ContainerTest) String() string {
func (container ContainerTest[T]) String() string {
str := "ContainerTest\n"
var values []string
for _, value := range container.values {
Expand All @@ -45,24 +45,32 @@ func (container ContainerTest) String() string {
}

func TestGetSortedValuesInts(t *testing.T) {
container := ContainerTest{}
GetSortedValues(container, utils.IntComparator)
container.values = []interface{}{5, 1, 3, 2, 4}
values := GetSortedValues(container, utils.IntComparator)
container := ContainerTest[int]{}
GetSortedValues(container)
container.values = []int{5, 1, 3, 2, 4}
values := GetSortedValues(container)
for i := 1; i < container.Size(); i++ {
if values[i-1].(int) > values[i].(int) {
if values[i-1] > values[i] {
t.Errorf("Not sorted!")
}
}
}

func TestGetSortedValuesStrings(t *testing.T) {
container := ContainerTest{}
GetSortedValues(container, utils.StringComparator)
container.values = []interface{}{"g", "a", "d", "e", "f", "c", "b"}
values := GetSortedValues(container, utils.StringComparator)
type NotInt struct {
i int
}

func TestGetSortedValuesNotInts(t *testing.T) {
container := ContainerTest[NotInt]{}
GetSortedValuesFunc(container, func(x, y NotInt) int {
return cmp.Compare(x.i, y.i)
})
container.values = []NotInt{{5}, {1}, {3}, {2}, {4}}
values := GetSortedValuesFunc(container, func(x, y NotInt) int {
return cmp.Compare(x.i, y.i)
})
for i := 1; i < container.Size(); i++ {
if values[i-1].(string) > values[i].(string) {
if values[i-1].i > values[i].i {
t.Errorf("Not sorted!")
}
}
Expand Down
20 changes: 10 additions & 10 deletions containers/enumerable.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
package containers

// EnumerableWithIndex provides functions for ordered containers whose values can be fetched by an index.
type EnumerableWithIndex interface {
type EnumerableWithIndex[T any] interface {
// Each calls the given function once for each element, passing that element's index and value.
Each(func(index int, value interface{}))
Each(func(index int, value T))

// Map invokes the given function once for each element and returns a
// container containing the values returned by the given function.
Expand All @@ -18,22 +18,22 @@ type EnumerableWithIndex interface {

// Any passes each element of the container to the given function and
// returns true if the function ever returns true for any element.
Any(func(index int, value interface{}) bool) bool
Any(func(index int, value T) bool) bool

// All passes each element of the container to the given function and
// returns true if the function returns true for all elements.
All(func(index int, value interface{}) bool) bool
All(func(index int, value T) bool) bool

// Find passes each element of the container to the given function and returns
// the first (index,value) for which the function is true or -1,nil otherwise
// if no element matches the criteria.
Find(func(index int, value interface{}) bool) (int, interface{})
Find(func(index int, value T) bool) (int, T)
}

// EnumerableWithKey provides functions for ordered containers whose values whose elements are key/value pairs.
type EnumerableWithKey interface {
type EnumerableWithKey[K, V any] interface {
// Each calls the given function once for each element, passing that element's key and value.
Each(func(key interface{}, value interface{}))
Each(func(key K, value V))

// Map invokes the given function once for each element and returns a container
// containing the values returned by the given function as key/value pairs.
Expand All @@ -44,14 +44,14 @@ type EnumerableWithKey interface {

// Any passes each element of the container to the given function and
// returns true if the function ever returns true for any element.
Any(func(key interface{}, value interface{}) bool) bool
Any(func(key K, value V) bool) bool

// All passes each element of the container to the given function and
// returns true if the function returns true for all elements.
All(func(key interface{}, value interface{}) bool) bool
All(func(key K, value V) bool) bool

// Find passes each element of the container to the given function and returns
// the first (key,value) for which the function is true or nil,nil otherwise if no element
// matches the criteria.
Find(func(key interface{}, value interface{}) bool) (interface{}, interface{})
Find(func(key K, value V) bool) (K, V)
}
30 changes: 15 additions & 15 deletions containers/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package containers

// IteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
type IteratorWithIndex interface {
type IteratorWithIndex[T any] interface {
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
Expand All @@ -14,7 +14,7 @@ type IteratorWithIndex interface {

// Value returns the current element's value.
// Does not modify the state of the iterator.
Value() interface{}
Value() T

// Index returns the current element's index.
// Does not modify the state of the iterator.
Expand All @@ -33,11 +33,11 @@ type IteratorWithIndex interface {
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
NextTo(func(index int, value interface{}) bool) bool
NextTo(func(index int, value T) bool) bool
}

// IteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
type IteratorWithKey interface {
type IteratorWithKey[K, V any] interface {
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
Expand All @@ -46,11 +46,11 @@ type IteratorWithKey interface {

// Value returns the current element's value.
// Does not modify the state of the iterator.
Value() interface{}
Value() V

// Key returns the current element's key.
// Does not modify the state of the iterator.
Key() interface{}
Key() K

// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
Expand All @@ -65,19 +65,19 @@ type IteratorWithKey interface {
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
NextTo(func(key interface{}, value interface{}) bool) bool
NextTo(func(key K, value V) bool) bool
}

// ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
//
// Essentially it is the same as IteratorWithIndex, but provides additional:
//
// Prev() function to enable traversal in reverse
// # Prev() function to enable traversal in reverse
//
// Last() function to move the iterator to the last element.
//
// End() function to move the iterator past the last element (one-past-the-end).
type ReverseIteratorWithIndex interface {
type ReverseIteratorWithIndex[T any] interface {
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
Expand All @@ -96,19 +96,19 @@ type ReverseIteratorWithIndex interface {
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
PrevTo(func(index int, value interface{}) bool) bool
PrevTo(func(index int, value T) bool) bool

IteratorWithIndex
IteratorWithIndex[T]
}

// ReverseIteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
//
// Essentially it is the same as IteratorWithKey, but provides additional:
//
// Prev() function to enable traversal in reverse
// # Prev() function to enable traversal in reverse
//
// Last() function to move the iterator to the last element.
type ReverseIteratorWithKey interface {
type ReverseIteratorWithKey[K, V any] interface {
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
Expand All @@ -127,7 +127,7 @@ type ReverseIteratorWithKey interface {
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
PrevTo(func(key interface{}, value interface{}) bool) bool
PrevTo(func(key K, value V) bool) bool

IteratorWithKey
IteratorWithKey[K, V]
}
9 changes: 5 additions & 4 deletions examples/arraylist/arraylist.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
package main

import (
"github.com/emirpasic/gods/lists/arraylist"
"github.com/emirpasic/gods/utils"
"cmp"

"github.com/emirpasic/gods/v2/lists/arraylist"
)

// ArrayListExample to demonstrate basic usage of ArrayList
func main() {
list := arraylist.New()
list := arraylist.New[string]()
list.Add("a") // ["a"]
list.Add("c", "b") // ["a","c","b"]
list.Sort(utils.StringComparator) // ["a","b","c"]
list.Sort(cmp.Compare[string]) // ["a","b","c"]
_, _ = list.Get(0) // "a",true
_, _ = list.Get(100) // nil,false
_ = list.Contains("a", "b", "c") // true
Expand Down
4 changes: 2 additions & 2 deletions examples/arrayqueue/arrayqqueue.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

package main

import aq "github.com/emirpasic/gods/queues/arrayqueue"
import aq "github.com/emirpasic/gods/v2/queues/arrayqueue"

// ArrayQueueExample to demonstrate basic usage of ArrayQueue
func main() {
queue := aq.New() // empty
queue := aq.New[int]() // empty
queue.Enqueue(1) // 1
queue.Enqueue(2) // 1, 2
_ = queue.Values() // 1, 2 (FIFO order)
Expand Down
26 changes: 13 additions & 13 deletions examples/arraystack/arraystack.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

package main

import "github.com/emirpasic/gods/stacks/arraystack"
import "github.com/emirpasic/gods/v2/stacks/arraystack"

// ArrayStackExample to demonstrate basic usage of ArrayStack
func main() {
stack := arraystack.New() // empty
stack.Push(1) // 1
stack.Push(2) // 1, 2
stack.Values() // 2, 1 (LIFO order)
_, _ = stack.Peek() // 2,true
_, _ = stack.Pop() // 2, true
_, _ = stack.Pop() // 1, true
_, _ = stack.Pop() // nil, false (nothing to pop)
stack.Push(1) // 1
stack.Clear() // empty
stack.Empty() // true
stack.Size() // 0
stack := arraystack.New[int]() // empty
stack.Push(1) // 1
stack.Push(2) // 1, 2
stack.Values() // 2, 1 (LIFO order)
_, _ = stack.Peek() // 2,true
_, _ = stack.Pop() // 2, true
_, _ = stack.Pop() // 1, true
_, _ = stack.Pop() // nil, false (nothing to pop)
stack.Push(1) // 1
stack.Clear() // empty
stack.Empty() // true
stack.Size() // 0
}
5 changes: 3 additions & 2 deletions examples/avltree/avltree.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ package main

import (
"fmt"
avl "github.com/emirpasic/gods/trees/avltree"

avl "github.com/emirpasic/gods/v2/trees/avltree"
)

// AVLTreeExample to demonstrate basic usage of AVLTree
func main() {
tree := avl.NewWithIntComparator() // empty(keys are of type int)
tree := avl.New[int, string]() // empty(keys are of type int)

tree.Put(1, "x") // 1->x
tree.Put(2, "b") // 1->x, 2->b (in order)
Expand Down

0 comments on commit 14f7142

Please sign in to comment.