diff --git a/Makefile b/Makefile index 090d6a2..c895621 100644 --- a/Makefile +++ b/Makefile @@ -12,3 +12,6 @@ dep: vet: go vet + +build: + go build -o ./build \ No newline at end of file diff --git a/pkg/collection/collection.go b/pkg/collection/collection.go new file mode 100644 index 0000000..5dff2b6 --- /dev/null +++ b/pkg/collection/collection.go @@ -0,0 +1,36 @@ +package collection + +type Iterator[E any] interface { + HasNext() bool + Next() E + NextWithIndex() (int, E) +} + +type Iterable[E any] interface { + Iterator() Iterator[E] +} + +type Collection[E any] interface { + Iterable[E] + Empty() bool + Size() int + Push(item E) + Contains(item E) bool + Delete(item E) error +} + +type List[E any] interface { + Collection[E] + Back() (E, error) + Front() (E, error) + PushBack(item E) + PushFront(item E) + Index(item E) (int, error) + GetAt(pos int) (E, error) + PushAt(item E, pos int) + DeleteAt(pos int) error +} + +type Set[E comparable] interface { + Collection[E] +} diff --git a/pkg/collection/errors.go b/pkg/collection/errors.go new file mode 100644 index 0000000..b396b5d --- /dev/null +++ b/pkg/collection/errors.go @@ -0,0 +1,26 @@ +package collection + +import "fmt" + +var ( + ErrPositionNegative = fmt.Errorf("position can not be negative") + ErrEmptyCollection = fmt.Errorf("this collection is empty") + ErrNodeNotFound = fmt.Errorf("node not found") +) + +type ErrIndexOutOfBound struct { + index int + size int +} + +func (e ErrIndexOutOfBound) Error() string { + return fmt.Sprintf("index %d out of bound from range %d and %d", e.index, 0, e.size-1) +} + +type ErrItemNotFound struct { + item any +} + +func (e ErrItemNotFound) Error() string { + return fmt.Sprintf("item %v not found", e.item) +} diff --git a/pkg/collection/iterator.go b/pkg/collection/iterator.go deleted file mode 100644 index d89e83a..0000000 --- a/pkg/collection/iterator.go +++ /dev/null @@ -1,11 +0,0 @@ -package collection - -type Iterator[E any] interface { - HasNext() bool - Next() E - NextWithIndex() (int, E) -} - -type Iterable[E any] interface { - Iterator() Iterator[E] -} diff --git a/pkg/collection/linked_list.go b/pkg/collection/linked_list.go index bb3240d..719dec8 100644 --- a/pkg/collection/linked_list.go +++ b/pkg/collection/linked_list.go @@ -2,25 +2,10 @@ package collection import ( "fmt" - "log" + "reflect" "strings" ) -var ( - ErrPositionNegative = fmt.Errorf("position can not be negative") - ErrEmptyList = fmt.Errorf("no nodes in list") - ErrNodeNotFound = fmt.Errorf("node not found") -) - -type ErrIndexOutOfBound struct { - index int - size int -} - -func (e ErrIndexOutOfBound) Error() string { - return fmt.Sprintf("index %d out of bound from range %d and %d", e.index, 0, e.size-1) -} - type node[E any] struct { value E prev *node[E] @@ -112,7 +97,7 @@ func (l *LinkedList[E]) GetAt(pos int) (E, error) { // findNode returns node at given position from linked list func (l *LinkedList[E]) findNode(pos int) (*node[E], error) { if l.Empty() { - return nil, ErrEmptyList + return nil, ErrEmptyCollection } ptr := l.head @@ -131,6 +116,30 @@ func (l *LinkedList[E]) findNode(pos int) (*node[E], error) { return ptr, nil } +func (l *LinkedList[E]) Contains(item E) bool { + for it := l.Iterator(); it.HasNext(); { + x := it.Next() + if reflect.DeepEqual(item, x) { + return true + } + } + return false +} + +func (l *LinkedList[E]) Index(item E) (int, error) { + for it := l.Iterator(); it.HasNext(); { + i, x := it.NextWithIndex() + if reflect.DeepEqual(item, x) { + return i, nil + } + } + return 0, ErrItemNotFound{item} +} + +func (l *LinkedList[E]) Push(item E) { + l.PushBack(item) +} + func (l *LinkedList[E]) PushFront(item E) { l.PushAt(item, 0) } @@ -175,17 +184,23 @@ func (l *LinkedList[E]) PushAt(item E, pos int) { l.size++ } +func (l *LinkedList[E]) Delete(item E) error { + pos, err := l.Index(item) + if err != nil { + return err + } + return l.DeleteAt(pos) +} + // DeleteAt deletes node at given position from linked list func (l *LinkedList[E]) DeleteAt(pos int) error { // validate the position if pos < 0 { - log.Println("position can not be negative") return ErrPositionNegative } if l.size == 0 { - log.Println("no nodes in list") - return ErrEmptyList + return ErrEmptyCollection } if pos == 0 { @@ -199,7 +214,6 @@ func (l *LinkedList[E]) DeleteAt(pos int) error { } else { prevNode, _ := l.findNode(pos - 1) if prevNode == nil { - log.Println("node not found") return ErrNodeNotFound } myNode, err := l.findNode(pos) diff --git a/pkg/collection/linked_list_test.go b/pkg/collection/linked_list_test.go index 2eee8be..574f359 100644 --- a/pkg/collection/linked_list_test.go +++ b/pkg/collection/linked_list_test.go @@ -54,14 +54,14 @@ func TestLinkedList_GetAt(t *testing.T) { {description: "get middle item", list: NewLinkedList(1, 2, 3), pos: 1, want: 2, err: nil}, {description: "get last item", list: NewLinkedList(1, 2, 3), pos: 2, want: 3, err: nil}, {description: "get last item", list: NewLinkedList(1, 2, 3), pos: 2, want: 3, err: nil}, - {description: "no items in list return zero value of the type", list: NewLinkedList[int](), pos: 0, want: 0, err: ErrEmptyList}, + {description: "no items in list return zero value of the type", list: NewLinkedList[int](), pos: 0, want: 0, err: ErrEmptyCollection}, {description: "get item with negative position", list: NewLinkedList(1, 2, 3), pos: -1, want: 0, err: ErrPositionNegative}, {description: "get item with index out of bound", list: NewLinkedList(1, 2, 3), pos: 4, want: 0, err: ErrIndexOutOfBound{4, 3}}, } for _, tt := range useCases { result, err := tt.list.GetAt(tt.pos) - if result != tt.want && err != tt.err { + if result != tt.want || err != tt.err { t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) } } @@ -74,14 +74,14 @@ func TestLinkedList_Back(t *testing.T) { err error want int }{ - {description: "no items in list return zero value of the type", list: NewLinkedList[int](), want: 0, err: ErrEmptyList}, + {description: "no items in list return zero value of the type", list: NewLinkedList[int](), want: 0, err: ErrEmptyCollection}, {description: "singleton list return first element", list: NewLinkedList(1), want: 1}, {description: "get last item", list: NewLinkedList(1, 2, 3), want: 3}, } for _, tt := range useCases { result, err := tt.list.Back() - if result != tt.want && err != tt.err { + if result != tt.want || err != tt.err { t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) } } @@ -94,19 +94,44 @@ func TestLinkedList_Front(t *testing.T) { err error want int }{ - {description: "no items in list return zero value of the type", list: NewLinkedList[int](), want: 0, err: ErrEmptyList}, + {description: "no items in list return zero value of the type", list: NewLinkedList[int](), want: 0, err: ErrEmptyCollection}, {description: "singleton list return first element", list: NewLinkedList(1), want: 1}, {description: "get first item", list: NewLinkedList(1, 2, 3), want: 1}, } for _, tt := range useCases { result, err := tt.list.Front() - if result != tt.want && err != tt.err { + if result != tt.want || err != tt.err { t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) } } } +func TestLinkedList_Push(t *testing.T) { + useCases := []struct { + description string + original *LinkedList[int] + modified *LinkedList[int] + item int + }{ + {description: "push item in empty slice", + original: NewLinkedList[int](), + modified: NewLinkedList(1), + item: 1}, + {description: "push item in slice with item in last position", + original: NewLinkedList(1, 2), + modified: NewLinkedList(1, 2, 3), + item: 3}, + } + + for _, tt := range useCases { + tt.original.Push(tt.item) + if !compareLists(tt.original, tt.modified) { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + func TestLinkedList_PushAt(t *testing.T) { useCases := []struct { description string @@ -205,6 +230,41 @@ func TestLinkedList_PushFront(t *testing.T) { } } +func TestLinkedList_Delete(t *testing.T) { + useCases := []struct { + description string + original *LinkedList[int] + modified *LinkedList[int] + item int + err error + }{ + {description: "delete item in empty list", + original: NewLinkedList[int](), + modified: NewLinkedList[int](), + item: 0, + err: ErrItemNotFound{0}}, + {description: "delete item in list with item in first position", + original: NewLinkedList(1, 2, 3), + modified: NewLinkedList(2, 3), + item: 1}, + {description: "delete item in list with item in middle position", + original: NewLinkedList(1, 2, 3), + modified: NewLinkedList(1, 3), + item: 2}, + {description: "delete item in list with item in last position", + original: NewLinkedList(1, 2, 3), + modified: NewLinkedList(1, 2), + item: 3}, + } + + for _, tt := range useCases { + err := tt.original.Delete(tt.item) + if !compareLists(tt.original, tt.modified) || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.modified, tt.err, tt.original, err) + } + } +} + func TestLinkedList_DeleteAt(t *testing.T) { useCases := []struct { description string @@ -215,17 +275,17 @@ func TestLinkedList_DeleteAt(t *testing.T) { }{ {description: "delete item with negative position", original: NewLinkedList[int](), - modified: nil, + modified: NewLinkedList[int](), pos: -1, err: ErrPositionNegative}, {description: "delete item in empty list", original: NewLinkedList[int](), - modified: nil, + modified: NewLinkedList[int](), pos: 0, - err: ErrEmptyList}, + err: ErrEmptyCollection}, {description: "delete item in position not found", original: NewLinkedList(1, 2, 3), - modified: NewLinkedList[int](), + modified: NewLinkedList(1, 2, 3), pos: 5, err: ErrNodeNotFound}, {description: "delete item in first position", @@ -247,8 +307,49 @@ func TestLinkedList_DeleteAt(t *testing.T) { for _, tt := range useCases { err := tt.original.DeleteAt(tt.pos) - if !compareLists(tt.original, tt.modified) && err != tt.err { - t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + if !compareLists(tt.original, tt.modified) || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.modified, tt.err, tt.original, err) + } + } +} + +func TestLinkedList_Contains(t *testing.T) { + useCases := []struct { + description string + list *LinkedList[int] + item int + want bool + }{ + {description: "empty linked list contain item", list: NewLinkedList[int](), item: 0, want: false}, + {description: "linked list with items search item no found", list: NewLinkedList[int](1, 2, 3), item: 0, want: false}, + {description: "linked list with items search item found", list: NewLinkedList[int](1, 2, 3), item: 3, want: true}, + } + + for _, tt := range useCases { + result := tt.list.Contains(tt.item) + if result != tt.want { + t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) + } + } +} + +func TestLinkedList_Index(t *testing.T) { + useCases := []struct { + description string + list *LinkedList[int] + item int + pos int + err error + }{ + {description: "empty linked list contain item", list: NewLinkedList[int](), item: 0, pos: 0, err: ErrItemNotFound{0}}, + {description: "linked list with items search item no found", list: NewLinkedList[int](1, 2, 3), item: 0, pos: 0, err: ErrItemNotFound{0}}, + {description: "linked list with items search item found", list: NewLinkedList[int](1, 2, 3), item: 3, pos: 2, err: nil}, + } + + for _, tt := range useCases { + pos, err := tt.list.Index(tt.item) + if pos != tt.pos || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.pos, tt.err, pos, err) } } } diff --git a/pkg/collection/slice.go b/pkg/collection/slice.go new file mode 100644 index 0000000..21bf2b8 --- /dev/null +++ b/pkg/collection/slice.go @@ -0,0 +1,176 @@ +package collection + +import ( + "fmt" + "math" + "reflect" + "strings" +) + +type sliceIterator[E any] struct { + index int + slice []E +} + +func (it *sliceIterator[E]) HasNext() bool { + return it.index < len(it.slice) +} + +func (it *sliceIterator[E]) Next() E { + item := it.slice[it.index] + it.index += 1 + return item +} + +func (it *sliceIterator[E]) NextWithIndex() (int, E) { + index := it.index + item := it.slice[index] + it.index += 1 + return index, item +} + +type Slice[E any] struct { + inner []E +} + +func NewSlice[E any](items ...E) *Slice[E] { + return &Slice[E]{items} +} + +func (s *Slice[E]) Iterator() Iterator[E] { + if s.Empty() { + return &emptyListIterator[E]{} + } + return &sliceIterator[E]{0, s.inner} +} + +func (s *Slice[E]) Empty() bool { + return s.Size() == 0 +} + +func (s *Slice[E]) Size() int { + return len(s.inner) +} + +func (s *Slice[E]) Back() (E, error) { + return s.GetAt(s.Size() - 1) +} + +func (s *Slice[E]) Front() (E, error) { + return s.GetAt(0) +} + +func (s *Slice[E]) GetAt(pos int) (E, error) { + if s.Empty() { + return *new(E), ErrEmptyCollection + } + if pos < 0 { + return *new(E), ErrPositionNegative + } + if pos < 0 || pos >= s.Size() { + return *new(E), ErrIndexOutOfBound{pos, s.Size()} + } + return s.inner[pos], nil +} + +func (s *Slice[E]) Push(item E) { + s.PushBack(item) +} + +func (s *Slice[E]) PushBack(item E) { + pos := int(math.Max(0.0, float64(s.Size()))) + s.PushAt(item, pos) +} + +func (s *Slice[E]) PushFront(item E) { + s.PushAt(item, 0) +} + +func (s *Slice[E]) PushAt(item E, pos int) { + s.inner = insert(s.inner, item, pos) +} + +func insert[E any](slice []E, item E, pos int) []E { + // found https://stackoverflow.com/questions/46128016/insert-a-value-in-a-slice-at-a-given-index + n := len(slice) + if pos < 0 { + pos = (pos%n + n) % n + } + switch { + case pos == n: // nil or empty slice or after last element + return append(slice, item) + + case pos < n: // pos < len(slice) + slice = append(slice[:pos+1], slice[pos:]...) + slice[pos] = item + return slice + + case pos < cap(slice): // pos > len(slice) + slice = slice[:pos+1] + var zero E + for i := n; i < pos; i++ { + slice[i] = zero + } + slice[pos] = item + return slice + + default: + b := make([]E, pos+1) // malloc + if n > 0 { + copy(b, slice) + } + b[pos] = item + return b + } +} + +func (s *Slice[E]) Delete(item E) error { + pos, err := s.Index(item) + if err != nil { + return err + } + return s.DeleteAt(pos) +} + +func (s *Slice[E]) DeleteAt(pos int) error { + if s.Empty() { + return ErrEmptyCollection + } + s.inner = append(s.inner[:pos], s.inner[pos+1:]...) + return nil +} + +func (s *Slice[E]) Contains(item E) bool { + for _, elem := range s.inner { + if reflect.DeepEqual(elem, item) { + return true + } + } + return false +} + +func (s *Slice[E]) Index(item E) (int, error) { + for i, x := range s.inner { + if reflect.DeepEqual(x, item) { + return i, nil + } + } + return 0, ErrItemNotFound{item} +} + +func (s *Slice[E]) String() string { + var sb strings.Builder + sb.WriteString("[") + for i := 0; i < s.Size(); i++ { + var str string + item, _ := s.GetAt(i) + if i >= s.Size()-1 { + str = fmt.Sprintf("%v", item) + } else { + str = fmt.Sprintf("%v, ", item) + } + sb.WriteString(str) + } + sb.WriteString("]") + return sb.String() +} diff --git a/pkg/collection/slice_test.go b/pkg/collection/slice_test.go new file mode 100644 index 0000000..fa01cc2 --- /dev/null +++ b/pkg/collection/slice_test.go @@ -0,0 +1,330 @@ +package collection + +import ( + "reflect" + "testing" +) + +func TestSlice_Empty(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + want bool + }{ + {description: "empty slice", slice: NewSlice[int](), want: true}, + {description: "full slice", slice: NewSlice[int](1, 2, 3), want: false}, + } + + for _, tt := range useCases { + result := tt.slice.Empty() + if result != tt.want { + t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) + } + } +} + +func TestSlice_Size(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + want int + }{ + {description: "empty slice", slice: NewSlice[int](), want: 0}, + {description: "full slice", slice: NewSlice[int](1, 2, 3), want: 3}, + } + + for _, tt := range useCases { + result := tt.slice.Size() + if result != tt.want { + t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) + } + } +} + +func TestSlice_GetAt(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + pos int + item int + err error + }{ + {description: "get item on empty slice", slice: NewSlice[int](), pos: 0, item: 0, err: ErrEmptyCollection}, + {description: "get item on full slice with out of bound index", slice: NewSlice[int](1, 2, 3), pos: 5, item: 0, err: ErrIndexOutOfBound{5, 3}}, + {description: "get item on full slice with negative index", slice: NewSlice[int](1, 2, 3), pos: -1, item: 0, err: ErrPositionNegative}, + {description: "get item on full slice in first position", slice: NewSlice[int](1, 2, 3), pos: 0, item: 1, err: nil}, + {description: "get item on full slice in middle position", slice: NewSlice[int](1, 2, 3), pos: 1, item: 2, err: nil}, + {description: "get item on full slice in last position", slice: NewSlice[int](1, 2, 3), pos: 2, item: 3, err: nil}, + } + + for _, tt := range useCases { + result, err := tt.slice.GetAt(tt.pos) + if result != tt.item || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.item, tt.err, result, err) + } + } +} + +func TestSlice_Back(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + pos int + item int + err error + }{ + {description: "get item on empty slice", slice: NewSlice[int](), pos: 0, item: 0, err: ErrEmptyCollection}, + {description: "get item on full slice in last position", slice: NewSlice[int](1, 2, 3), pos: 2, item: 3, err: nil}, + } + + for _, tt := range useCases { + result, err := tt.slice.Back() + if result != tt.item || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.item, tt.err, result, err) + } + } +} + +func TestSlice_Front(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + pos int + item int + err error + }{ + {description: "get item on empty slice", slice: NewSlice[int](), pos: 0, item: 0, err: ErrEmptyCollection}, + {description: "get item on full slice in first position", slice: NewSlice[int](1, 2, 3), pos: 2, item: 1, err: nil}, + } + + for _, tt := range useCases { + result, err := tt.slice.Front() + if result != tt.item || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.item, tt.err, result, err) + } + } +} + +func TestSlice_PushAt(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + item int + pos int + }{ + {description: "push item in empty slice", + original: NewSlice[int](), + modified: NewSlice(1), + item: 1, + pos: 0}, + {description: "push item in slice with item in first position", + original: NewSlice(1, 2, 3), + modified: NewSlice(0, 1, 2, 3), + item: 0, + pos: 0}, + {description: "push item in slice with item in middle position", + original: NewSlice(1, 3), + modified: NewSlice(1, 2, 3), + item: 2, + pos: 1}, + {description: "push item in slice with item in last position", + original: NewSlice(1, 2), + modified: NewSlice(1, 2, 3), + item: 3, + pos: 2}, + } + + for _, tt := range useCases { + tt.original.PushAt(tt.item, tt.pos) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_Push(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + item int + }{ + {description: "push item in empty slice", + original: NewSlice[int](), + modified: NewSlice(1), + item: 1}, + {description: "push item in slice with item in last position", + original: NewSlice(1, 2), + modified: NewSlice(1, 2, 3), + item: 3}, + } + + for _, tt := range useCases { + tt.original.Push(tt.item) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_PushBack(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + item int + }{ + {description: "push item in empty slice", + original: NewSlice[int](), + modified: NewSlice(1), + item: 1}, + {description: "push item in slice with item in last position", + original: NewSlice(1, 2), + modified: NewSlice(1, 2, 3), + item: 3}, + } + + for _, tt := range useCases { + tt.original.PushBack(tt.item) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_PushFront(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + item int + }{ + {description: "push item in empty slice", + original: NewSlice[int](), + modified: NewSlice(1), + item: 1}, + {description: "push item in slice with item in first position", + original: NewSlice(2, 3), + modified: NewSlice(1, 2, 3), + item: 1}, + } + + for _, tt := range useCases { + tt.original.PushFront(tt.item) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_Delete(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + item int + err error + }{ + {description: "delete item in empty slice", + original: NewSlice[int](), + modified: NewSlice[int](), + item: 0, + err: ErrItemNotFound{0}}, + {description: "delete item in slice with item in first position", + original: NewSlice(1, 2, 3), + modified: NewSlice(2, 3), + item: 1}, + {description: "delete item in slice with item in middle position", + original: NewSlice(1, 2, 3), + modified: NewSlice(1, 3), + item: 2}, + {description: "delete item in slice with item in last position", + original: NewSlice(1, 2, 3), + modified: NewSlice(1, 2), + item: 3}, + } + + for _, tt := range useCases { + err := tt.original.Delete(tt.item) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) || err != tt.err { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_DeleteAt(t *testing.T) { + useCases := []struct { + description string + original *Slice[int] + modified *Slice[int] + pos int + err error + }{ + {description: "delete item in empty slice", + original: NewSlice[int](), + modified: NewSlice[int](), + pos: 0, + err: ErrEmptyCollection}, + {description: "delete item in slice with item in first position", + original: NewSlice(1, 2, 3), + modified: NewSlice(2, 3), + pos: 0}, + {description: "delete item in slice with item in middle position", + original: NewSlice(1, 2, 3), + modified: NewSlice(1, 3), + pos: 1}, + {description: "delete item in slice with item in last position", + original: NewSlice(1, 2, 3), + modified: NewSlice(1, 2), + pos: 2}, + } + + for _, tt := range useCases { + err := tt.original.DeleteAt(tt.pos) + if !reflect.DeepEqual(tt.original.inner, tt.modified.inner) || err != tt.err { + t.Errorf("test: %s want %v got %v", tt.description, tt.modified, tt.original) + } + } +} + +func TestSlice_Contains(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + item int + want bool + }{ + {description: "empty slice contain item", slice: NewSlice[int](), item: 0, want: false}, + {description: "slice with items search item no found", slice: NewSlice[int](1, 2, 3), item: 0, want: false}, + {description: "slice with items search item found", slice: NewSlice[int](1, 2, 3), item: 3, want: true}, + } + + for _, tt := range useCases { + result := tt.slice.Contains(tt.item) + if result != tt.want { + t.Errorf("test: %s want %v got %v", tt.description, tt.want, result) + } + } +} + +func TestSlice_Index(t *testing.T) { + useCases := []struct { + description string + slice *Slice[int] + item int + pos int + err error + }{ + {description: "empty slice contain item", slice: NewSlice[int](), item: 0, pos: 0, err: ErrItemNotFound{0}}, + {description: "slice with items search item no found", slice: NewSlice[int](1, 2, 3), item: 0, pos: 0, err: ErrItemNotFound{0}}, + {description: "slice with items search item found", slice: NewSlice[int](1, 2, 3), item: 3, pos: 2, err: nil}, + } + + for _, tt := range useCases { + pos, err := tt.slice.Index(tt.item) + if pos != tt.pos || err != tt.err { + t.Errorf("test: %s want {%v, %v} got {%v, %v}", tt.description, tt.pos, tt.err, pos, err) + } + } +} diff --git a/pkg/collection/stack.go b/pkg/collection/stack.go index 05ce936..d95b4a3 100644 --- a/pkg/collection/stack.go +++ b/pkg/collection/stack.go @@ -5,11 +5,6 @@ import ( "strings" ) -var ( - // ErrEmptyStack is error when you have an empty stack structure - ErrEmptyStack = fmt.Errorf("this stack is empty") -) - // Stack is an structure with method for handle underlying slice as a stack. type Stack[E any] struct { items []E @@ -35,16 +30,16 @@ func (s *Stack[E]) Empty() bool { } // Top get first item in the stack structure, but -// if stack is empty well this method return ErrEmptyStack error. +// if stack is empty well this method return ErrEmptyCollection error. func (s *Stack[E]) Top() (E, error) { if s.Empty() { - return *new(E), ErrEmptyStack + return *new(E), ErrEmptyCollection } return s.items[s.Size()-1], nil } // Pop get and remove first item in the stack structure, but -// if stack is empty well this method return ErrEmptyStack error. +// if stack is empty well this method return ErrEmptyCollection error. func (s *Stack[E]) Pop() (E, error) { item, err := s.Top() if err != nil { diff --git a/pkg/collection/stack_test.go b/pkg/collection/stack_test.go index eb0e0ef..8e4fed1 100644 --- a/pkg/collection/stack_test.go +++ b/pkg/collection/stack_test.go @@ -16,7 +16,7 @@ var staticTests = []struct { size int top pair }{ - {"empty stack", NewStack[int](), true, 0, pair{0, ErrEmptyStack}}, + {"empty stack", NewStack[int](), true, 0, pair{0, ErrEmptyCollection}}, {"stack with items", NewStack(1, 2, 3), false, 3, pair{3, nil}}, } @@ -25,7 +25,7 @@ var popTests = []struct { input *Stack[int] item pair }{ - {"empty stack pop item", NewStack[int](), pair{0, ErrEmptyStack}}, + {"empty stack pop item", NewStack[int](), pair{0, ErrEmptyCollection}}, {"stack with items pop item", NewStack(1, 2, 3), pair{3, nil}}, }