From 08ae493e8a735a88bbef2b02ed737845c20f00ea Mon Sep 17 00:00:00 2001 From: Emir Pasic Date: Tue, 12 Apr 2022 01:20:56 +0200 Subject: [PATCH] Implement NextTo and PrevTo for all iterators and containers (index or key, forward-only or reversable) --- README.md | 76 +++++++++++- containers/iterator.go | 24 ++++ .../iteratorwithindex/iteratorwithindex.go | 29 +++++ examples/iteratorwithkey/iteratorwithkey.go | 36 +++++- lists/arraylist/arraylist_test.go | 101 ++++++++++++++++ lists/arraylist/iterator.go | 28 +++++ .../doublylinkedlist/doublylinkedlist_test.go | 101 ++++++++++++++++ lists/doublylinkedlist/iterator.go | 28 +++++ lists/singlylinkedlist/iterator.go | 14 +++ .../singlylinkedlist/singlylinkedlist_test.go | 50 ++++++++ maps/linkedhashmap/iterator.go | 28 +++++ maps/linkedhashmap/linkedhashmap_test.go | 109 ++++++++++++++++- maps/treebidimap/iterator.go | 28 +++++ maps/treebidimap/treebidimap_test.go | 109 ++++++++++++++++- maps/treemap/iterator.go | 28 +++++ maps/treemap/treemap_test.go | 109 ++++++++++++++++- sets/linkedhashset/iterator.go | 28 +++++ sets/linkedhashset/linkedhashset_test.go | 101 ++++++++++++++++ sets/treeset/iterator.go | 28 +++++ sets/treeset/treeset_test.go | 101 ++++++++++++++++ stacks/arraystack/arraystack_test.go | 107 +++++++++++++++++ stacks/arraystack/iterator.go | 28 +++++ stacks/linkedliststack/iterator.go | 14 +++ .../linkedliststack/linkedliststack_test.go | 53 +++++++++ trees/avltree/avltree_test.go | 107 +++++++++++++++++ trees/avltree/iterator.go | 28 +++++ trees/binaryheap/binaryheap_test.go | 111 +++++++++++++++++- trees/binaryheap/iterator.go | 28 +++++ trees/btree/btree_test.go | 107 +++++++++++++++++ trees/btree/iterator.go | 28 +++++ trees/redblacktree/iterator.go | 28 +++++ trees/redblacktree/redblacktree_test.go | 107 +++++++++++++++++ 32 files changed, 1889 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c76b8f3..fa67f21 100644 --- a/README.md +++ b/README.md @@ -983,6 +983,22 @@ for it.Begin(); it.Next(); { } ``` +Seeking to a specific element: + +```go +// Seek function, i.e. find element starting with "b" +seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") +} + +// Seek to the condition and continue traversal from that point (forward). +// assumes it.Begin() was called. +for found := it.NextTo(seek); found; found = it.Next() { + index, value := it.Index(), it.Value() + ... +} +``` + #### IteratorWithKey An [iterator](#iterator) whose elements are referenced by a key. @@ -1010,6 +1026,22 @@ for it.Begin(); it.Next(); { } ``` +Seeking to a specific element from the current iterator position: + +```go +// Seek function, i.e. find element starting with "b" +seek := func(key interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") +} + +// Seek to the condition and continue traversal from that point (forward). +// assumes it.Begin() was called. +for found := it.NextTo(seek); found; found = it.Next() { + key, value := it.Key(), it.Value() + ... +} +``` + #### ReverseIteratorWithIndex An [iterator](#iterator) whose elements are referenced by an index. Provides all functions as [IteratorWithIndex](#iteratorwithindex), but can also be used for reverse iteration. @@ -1031,6 +1063,22 @@ if it.Last() { } ``` +Seeking to a specific element: + +```go +// Seek function, i.e. find element starting with "b" +seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") +} + +// Seek to the condition and continue traversal from that point (in reverse). +// assumes it.End() was called. +for found := it.PrevTo(seek); found; found = it.Prev() { + index, value := it.Index(), it.Value() + ... +} +``` + #### ReverseIteratorWithKey An [iterator](#iterator) whose elements are referenced by a key. Provides all functions as [IteratorWithKey](#iteratorwithkey), but can also be used for reverse iteration. @@ -1052,6 +1100,20 @@ if it.Last() { } ``` +```go +// Seek function, i.e. find element starting with "b" +seek := func(key interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") +} + +// Seek to the condition and continue traversal from that point (in reverse). +// assumes it.End() was called. +for found := it.PrevTo(seek); found; found = it.Prev() { + key, value := it.Key(), it.Value() + ... +} +``` + ### Enumerable Enumerable functions for ordered containers that implement [EnumerableWithIndex](#enumerablewithindex) or [EnumerableWithKey](#enumerablewithkey) interfaces. @@ -1489,13 +1551,19 @@ Coding style: ```shell # Install tooling and set path: -go get github.com/golang/lint/golint -go get github.com/fzipp/gocyclo -go get github.com/kisielk/errcheck +go install gotest.tools/gotestsum@latest +go install golang.org/x/lint/golint@latest +go install github.com/kisielk/errcheck@latest export PATH=$PATH:$GOPATH/bin # Fix errors and warnings: -go fmt ./... && gofmt -s -w . && go vet ./... && go get ./... && go test ./... && golint ./... && gocyclo -avg -over 15 . && errcheck ./... +go fmt ./... && +go test -v ./... && +golint -set_exit_status ./... && +! go fmt ./... 2>&1 | read && +go vet -v ./... && +gocyclo -avg -over 15 ../gods && +errcheck ./... ``` ### License diff --git a/containers/iterator.go b/containers/iterator.go index f1a52a3..73994ec 100644 --- a/containers/iterator.go +++ b/containers/iterator.go @@ -28,6 +28,12 @@ type IteratorWithIndex interface { // If First() returns true, then first element's index and value can be retrieved by Index() and Value(). // Modifies the state of the iterator. First() bool + + // NextTo moves the iterator to the next element from current position that satisfies the condition given by the + // 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 } // IteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs. @@ -54,6 +60,12 @@ type IteratorWithKey interface { // If First() returns true, then first element's key and value can be retrieved by Key() and Value(). // Modifies the state of the iterator. First() bool + + // NextTo moves the iterator to the next element from current position that satisfies the condition given by the + // 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 } // ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index. @@ -80,6 +92,12 @@ type ReverseIteratorWithIndex interface { // Modifies the state of the iterator. Last() bool + // PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the + // 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 + IteratorWithIndex } @@ -105,5 +123,11 @@ type ReverseIteratorWithKey interface { // Modifies the state of the iterator. Last() bool + // PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the + // 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 + IteratorWithKey } diff --git a/examples/iteratorwithindex/iteratorwithindex.go b/examples/iteratorwithindex/iteratorwithindex.go index d15c439..4cbc87e 100644 --- a/examples/iteratorwithindex/iteratorwithindex.go +++ b/examples/iteratorwithindex/iteratorwithindex.go @@ -7,6 +7,7 @@ package main import ( "fmt" "github.com/emirpasic/gods/sets/treeset" + "strings" ) // IteratorWithIndexExample to demonstrate basic usage of IteratorWithIndex @@ -48,4 +49,32 @@ func main() { fmt.Print("\nLast index: ", it.Index()) // Last index: 3 fmt.Print("\nLast value: ", it.Value()) // Last value: c } + + // Seek element starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + it.Begin() + for found := it.NextTo(seek); found; found = it.Next() { + fmt.Print("\nNextTo index: ", it.Index()) + fmt.Print("\nNextTo value: ", it.Value()) + } /* + NextTo index: 1 + NextTo value: "b" + NextTo index: 2 + NextTo value: "c" + */ + + it.End() + for found := it.PrevTo(seek); found; found = it.Prev() { + fmt.Print("\nNextTo index: ", it.Index()) + fmt.Print("\nNextTo value: ", it.Value()) + } /* + NextTo index: 1 + NextTo value: "b" + NextTo index: 0 + NextTo value: "a" + */ + } diff --git a/examples/iteratorwithkey/iteratorwithkey.go b/examples/iteratorwithkey/iteratorwithkey.go index 4efeb7e..521e43b 100644 --- a/examples/iteratorwithkey/iteratorwithkey.go +++ b/examples/iteratorwithkey/iteratorwithkey.go @@ -7,14 +7,15 @@ package main import ( "fmt" "github.com/emirpasic/gods/maps/treemap" + "strings" ) // IteratorWithKeyExample to demonstrate basic usage of IteratorWithKey func main() { m := treemap.NewWithIntComparator() - m.Put(1, "a") - m.Put(2, "b") - m.Put(3, "a") + m.Put(0, "a") + m.Put(1, "b") + m.Put(2, "c") it := m.Iterator() fmt.Print("\nForward iteration\n") @@ -47,7 +48,34 @@ func main() { } if it.Last() { - fmt.Print("\nLast key: ", it.Key()) // Last key: 3 + fmt.Print("\nLast key: ", it.Key()) // Last key: 2 fmt.Print("\nLast value: ", it.Value()) // Last value: c } + + // Seek key-value pair whose value starts with "b" + seek := func(key interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + it.Begin() + for found := it.NextTo(seek); found; found = it.Next() { + fmt.Print("\nNextTo key: ", it.Key()) + fmt.Print("\nNextTo value: ", it.Value()) + } /* + NextTo key: 1 + NextTo value: "b" + NextTo key: 2 + NextTo value: "c" + */ + + it.End() + for found := it.PrevTo(seek); found; found = it.Prev() { + fmt.Print("\nNextTo key: ", it.Key()) + fmt.Print("\nNextTo value: ", it.Value()) + } /* + NextTo key: 1 + NextTo value: "b" + NextTo key: 0 + NextTo value: "a" + */ } diff --git a/lists/arraylist/arraylist_test.go b/lists/arraylist/arraylist_test.go index 63d97d4..88d7689 100644 --- a/lists/arraylist/arraylist_test.go +++ b/lists/arraylist/arraylist_test.go @@ -7,6 +7,7 @@ package arraylist import ( "fmt" "github.com/emirpasic/gods/utils" + "strings" "testing" ) @@ -500,6 +501,106 @@ func TestListIteratorLast(t *testing.T) { } } +func TestListIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + list := New() + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (not found) + { + list := New() + list.Add("xx", "yy") + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (found) + { + list := New() + list.Add("aa", "bb", "cc") + it := list.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestListIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + list := New() + it := list.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (not found) + { + list := New() + list.Add("xx", "yy") + it := list.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (found) + { + list := New() + list.Add("aa", "bb", "cc") + it := list.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestListSerialization(t *testing.T) { list := New() list.Add("a", "b", "c") diff --git a/lists/arraylist/iterator.go b/lists/arraylist/iterator.go index 38a93f3..2fefbb6 100644 --- a/lists/arraylist/iterator.go +++ b/lists/arraylist/iterator.go @@ -81,3 +81,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/lists/doublylinkedlist/doublylinkedlist_test.go b/lists/doublylinkedlist/doublylinkedlist_test.go index 596bd2e..2e6b0c9 100644 --- a/lists/doublylinkedlist/doublylinkedlist_test.go +++ b/lists/doublylinkedlist/doublylinkedlist_test.go @@ -6,6 +6,7 @@ package doublylinkedlist import ( "fmt" + "strings" "testing" "github.com/emirpasic/gods/utils" @@ -506,6 +507,106 @@ func TestListIteratorLast(t *testing.T) { } } +func TestListIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + list := New() + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (not found) + { + list := New() + list.Add("xx", "yy") + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (found) + { + list := New() + list.Add("aa", "bb", "cc") + it := list.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestListIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + list := New() + it := list.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (not found) + { + list := New() + list.Add("xx", "yy") + it := list.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (found) + { + list := New() + list.Add("aa", "bb", "cc") + it := list.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestListSerialization(t *testing.T) { list := New() list.Add("a", "b", "c") diff --git a/lists/doublylinkedlist/iterator.go b/lists/doublylinkedlist/iterator.go index 040921d..55fb0bc 100644 --- a/lists/doublylinkedlist/iterator.go +++ b/lists/doublylinkedlist/iterator.go @@ -102,3 +102,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/lists/singlylinkedlist/iterator.go b/lists/singlylinkedlist/iterator.go index 48605c2..5f25ff1 100644 --- a/lists/singlylinkedlist/iterator.go +++ b/lists/singlylinkedlist/iterator.go @@ -68,3 +68,17 @@ func (iterator *Iterator) First() bool { iterator.Begin() return iterator.Next() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/lists/singlylinkedlist/singlylinkedlist_test.go b/lists/singlylinkedlist/singlylinkedlist_test.go index 9b6fe59..3023a8e 100644 --- a/lists/singlylinkedlist/singlylinkedlist_test.go +++ b/lists/singlylinkedlist/singlylinkedlist_test.go @@ -6,6 +6,7 @@ package singlylinkedlist import ( "fmt" + "strings" "testing" "github.com/emirpasic/gods/utils" @@ -420,6 +421,55 @@ func TestListIteratorFirst(t *testing.T) { } } +func TestListIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + list := New() + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (not found) + { + list := New() + list.Add("xx", "yy") + it := list.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (found) + { + list := New() + list.Add("aa", "bb", "cc") + it := list.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + func TestListSerialization(t *testing.T) { list := New() list.Add("a", "b", "c") diff --git a/maps/linkedhashmap/iterator.go b/maps/linkedhashmap/iterator.go index d846efc..17f3bb3 100644 --- a/maps/linkedhashmap/iterator.go +++ b/maps/linkedhashmap/iterator.go @@ -79,3 +79,31 @@ func (iterator *Iterator) First() bool { func (iterator *Iterator) Last() bool { return iterator.iterator.Last() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/maps/linkedhashmap/linkedhashmap_test.go b/maps/linkedhashmap/linkedhashmap_test.go index 78437f1..912e7f3 100644 --- a/maps/linkedhashmap/linkedhashmap_test.go +++ b/maps/linkedhashmap/linkedhashmap_test.go @@ -6,6 +6,7 @@ package linkedhashmap import ( "fmt" + "strings" "testing" ) @@ -399,7 +400,7 @@ func TestMapIteratorBegin(t *testing.T) { } } -func TestMapTreeIteratorEnd(t *testing.T) { +func TestMapIteratorEnd(t *testing.T) { m := New() it := m.Iterator() m.Put(3, "c") @@ -440,6 +441,112 @@ func TestMapIteratorLast(t *testing.T) { } } +func TestMapIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + m := New() + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (not found) + { + m := New() + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (found) + { + m := New() + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestMapIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + m := New() + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (not found) + { + m := New() + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (found) + { + m := New() + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestMapSerialization(t *testing.T) { for i := 0; i < 10; i++ { original := New() diff --git a/maps/treebidimap/iterator.go b/maps/treebidimap/iterator.go index af9e27a..72e9983 100644 --- a/maps/treebidimap/iterator.go +++ b/maps/treebidimap/iterator.go @@ -75,3 +75,31 @@ func (iterator *Iterator) First() bool { func (iterator *Iterator) Last() bool { return iterator.iterator.Last() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/maps/treebidimap/treebidimap_test.go b/maps/treebidimap/treebidimap_test.go index 686b578..e1150fc 100644 --- a/maps/treebidimap/treebidimap_test.go +++ b/maps/treebidimap/treebidimap_test.go @@ -7,6 +7,7 @@ package treebidimap import ( "fmt" "github.com/emirpasic/gods/utils" + "strings" "testing" ) @@ -432,7 +433,7 @@ func TestMapIteratorBegin(t *testing.T) { } } -func TestMapTreeIteratorEnd(t *testing.T) { +func TestMapIteratorEnd(t *testing.T) { m := NewWith(utils.IntComparator, utils.StringComparator) it := m.Iterator() m.Put(3, "c") @@ -473,6 +474,112 @@ func TestMapIteratorLast(t *testing.T) { } } +func TestMapIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (not found) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (found) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestMapIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (not found) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (found) + { + m := NewWith(utils.IntComparator, utils.StringComparator) + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestMapSerialization(t *testing.T) { for i := 0; i < 10; i++ { original := NewWith(utils.StringComparator, utils.StringComparator) diff --git a/maps/treemap/iterator.go b/maps/treemap/iterator.go index 02b5c75..e7950a1 100644 --- a/maps/treemap/iterator.go +++ b/maps/treemap/iterator.go @@ -75,3 +75,31 @@ func (iterator *Iterator) First() bool { func (iterator *Iterator) Last() bool { return iterator.iterator.Last() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/maps/treemap/treemap_test.go b/maps/treemap/treemap_test.go index 0ee162e..05155a2 100644 --- a/maps/treemap/treemap_test.go +++ b/maps/treemap/treemap_test.go @@ -6,6 +6,7 @@ package treemap import ( "fmt" + "strings" "testing" ) @@ -455,7 +456,7 @@ func TestMapIteratorBegin(t *testing.T) { } } -func TestMapTreeIteratorEnd(t *testing.T) { +func TestMapIteratorEnd(t *testing.T) { m := NewWithIntComparator() it := m.Iterator() m.Put(3, "c") @@ -496,6 +497,112 @@ func TestMapIteratorLast(t *testing.T) { } } +func TestMapIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + m := NewWithIntComparator() + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (not found) + { + m := NewWithIntComparator() + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // NextTo (found) + { + m := NewWithIntComparator() + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestMapIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + m := NewWithIntComparator() + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (not found) + { + m := NewWithIntComparator() + m.Put(0, "xx") + m.Put(1, "yy") + it := m.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + } + + // PrevTo (found) + { + m := NewWithIntComparator() + m.Put(0, "aa") + m.Put(1, "bb") + m.Put(2, "cc") + it := m.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty map") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestMapSerialization(t *testing.T) { for i := 0; i < 10; i++ { original := NewWithStringComparator() diff --git a/sets/linkedhashset/iterator.go b/sets/linkedhashset/iterator.go index 3d190d9..5aa176c 100644 --- a/sets/linkedhashset/iterator.go +++ b/sets/linkedhashset/iterator.go @@ -75,3 +75,31 @@ func (iterator *Iterator) First() bool { func (iterator *Iterator) Last() bool { return iterator.iterator.Last() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/sets/linkedhashset/linkedhashset_test.go b/sets/linkedhashset/linkedhashset_test.go index 10b6da2..b77e5ec 100644 --- a/sets/linkedhashset/linkedhashset_test.go +++ b/sets/linkedhashset/linkedhashset_test.go @@ -6,6 +6,7 @@ package linkedhashset import ( "fmt" + "strings" "testing" ) @@ -332,6 +333,106 @@ func TestSetIteratorLast(t *testing.T) { } } +func TestSetIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + set := New() + it := set.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // NextTo (not found) + { + set := New() + set.Add("xx", "yy") + it := set.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // NextTo (found) + { + set := New() + set.Add("aa", "bb", "cc") + it := set.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestSetIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + set := New() + it := set.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // PrevTo (not found) + { + set := New() + set.Add("xx", "yy") + it := set.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // PrevTo (found) + { + set := New() + set.Add("aa", "bb", "cc") + it := set.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestSetSerialization(t *testing.T) { set := New() set.Add("a", "b", "c") diff --git a/sets/treeset/iterator.go b/sets/treeset/iterator.go index 9dd6289..377d8b6 100644 --- a/sets/treeset/iterator.go +++ b/sets/treeset/iterator.go @@ -87,3 +87,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/sets/treeset/treeset_test.go b/sets/treeset/treeset_test.go index b3cfca9..8571887 100644 --- a/sets/treeset/treeset_test.go +++ b/sets/treeset/treeset_test.go @@ -6,6 +6,7 @@ package treeset import ( "fmt" + "strings" "testing" ) @@ -341,6 +342,106 @@ func TestSetIteratorLast(t *testing.T) { } } +func TestSetIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + set := NewWithStringComparator() + it := set.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // NextTo (not found) + { + set := NewWithStringComparator() + set.Add("xx", "yy") + it := set.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // NextTo (found) + { + set := NewWithStringComparator() + set.Add("aa", "bb", "cc") + it := set.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestSetIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + set := NewWithStringComparator() + it := set.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // PrevTo (not found) + { + set := NewWithStringComparator() + set.Add("xx", "yy") + it := set.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + } + + // PrevTo (found) + { + set := NewWithStringComparator() + set.Add("aa", "bb", "cc") + it := set.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty set") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestSetSerialization(t *testing.T) { set := NewWithStringComparator() set.Add("a", "b", "c") diff --git a/stacks/arraystack/arraystack_test.go b/stacks/arraystack/arraystack_test.go index 62ba7fd..0596217 100644 --- a/stacks/arraystack/arraystack_test.go +++ b/stacks/arraystack/arraystack_test.go @@ -6,6 +6,7 @@ package arraystack import ( "fmt" + "strings" "testing" ) @@ -232,6 +233,112 @@ func TestStackIteratorLast(t *testing.T) { } } +func TestStackIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + stack := New() + it := stack.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // NextTo (not found) + { + stack := New() + stack.Push("xx") + stack.Push("yy") + it := stack.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // NextTo (found) + { + stack := New() + stack.Push("aa") + stack.Push("bb") + stack.Push("cc") + it := stack.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "aa") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestStackIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + stack := New() + it := stack.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // PrevTo (not found) + { + stack := New() + stack.Push("xx") + stack.Push("yy") + it := stack.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // PrevTo (found) + { + stack := New() + stack.Push("aa") + stack.Push("bb") + stack.Push("cc") + it := stack.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "cc") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestStackSerialization(t *testing.T) { stack := New() stack.Push("a") diff --git a/stacks/arraystack/iterator.go b/stacks/arraystack/iterator.go index 48f9bf8..df592f0 100644 --- a/stacks/arraystack/iterator.go +++ b/stacks/arraystack/iterator.go @@ -82,3 +82,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/stacks/linkedliststack/iterator.go b/stacks/linkedliststack/iterator.go index 4c7eafa..875f922 100644 --- a/stacks/linkedliststack/iterator.go +++ b/stacks/linkedliststack/iterator.go @@ -58,3 +58,17 @@ func (iterator *Iterator) First() bool { iterator.Begin() return iterator.Next() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/stacks/linkedliststack/linkedliststack_test.go b/stacks/linkedliststack/linkedliststack_test.go index f7b6f95..aeebc31 100644 --- a/stacks/linkedliststack/linkedliststack_test.go +++ b/stacks/linkedliststack/linkedliststack_test.go @@ -6,6 +6,7 @@ package linkedliststack import ( "fmt" + "strings" "testing" ) @@ -148,6 +149,58 @@ func TestStackIteratorFirst(t *testing.T) { } } +func TestStackIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + stack := New() + it := stack.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // NextTo (not found) + { + stack := New() + stack.Push("xx") + stack.Push("yy") + it := stack.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + } + + // NextTo (found) + { + stack := New() + stack.Push("aa") + stack.Push("bb") + stack.Push("cc") + it := stack.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty stack") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "aa") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + func TestStackSerialization(t *testing.T) { stack := New() stack.Push("a") diff --git a/trees/avltree/avltree_test.go b/trees/avltree/avltree_test.go index 99c2adf..16bf301 100644 --- a/trees/avltree/avltree_test.go +++ b/trees/avltree/avltree_test.go @@ -5,6 +5,7 @@ package avltree import ( "fmt" + "strings" "testing" ) @@ -556,6 +557,112 @@ func TestAVLTreeIteratorLast(t *testing.T) { } } +func TestAVLTreeIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + tree := NewWithIntComparator() + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (not found) + { + tree := NewWithIntComparator() + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (found) + { + tree := NewWithIntComparator() + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestAVLTreeIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + tree := NewWithIntComparator() + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (not found) + { + tree := NewWithIntComparator() + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (found) + { + tree := NewWithIntComparator() + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestAVLTreeSerialization(t *testing.T) { tree := NewWithStringComparator() tree.Put("c", "3") diff --git a/trees/avltree/iterator.go b/trees/avltree/iterator.go index bf2ff14..d4a1585 100644 --- a/trees/avltree/iterator.go +++ b/trees/avltree/iterator.go @@ -115,3 +115,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/trees/binaryheap/binaryheap_test.go b/trees/binaryheap/binaryheap_test.go index cf97813..7a3d060 100644 --- a/trees/binaryheap/binaryheap_test.go +++ b/trees/binaryheap/binaryheap_test.go @@ -6,6 +6,7 @@ package binaryheap import ( "math/rand" + "strings" "testing" ) @@ -198,7 +199,7 @@ func TestBinaryHeapIteratorBegin(t *testing.T) { } } -func TestListIteratorEnd(t *testing.T) { +func TestBinaryHeapIteratorEnd(t *testing.T) { heap := NewWithIntComparator() it := heap.Iterator() @@ -225,7 +226,7 @@ func TestListIteratorEnd(t *testing.T) { } } -func TestStackIteratorFirst(t *testing.T) { +func TestBinaryHeapIteratorFirst(t *testing.T) { heap := NewWithIntComparator() it := heap.Iterator() if actualValue, expectedValue := it.First(), false; actualValue != expectedValue { @@ -259,6 +260,112 @@ func TestBinaryHeapIteratorLast(t *testing.T) { } } +func TestBinaryHeapIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + tree := NewWithStringComparator() + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (not found) + { + tree := NewWithStringComparator() + tree.Push("xx") + tree.Push("yy") + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // NextTo (found) + { + tree := NewWithStringComparator() + tree.Push("aa") + tree.Push("bb") + tree.Push("cc") + it := tree.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestBinaryHeapIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index int, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + tree := NewWithStringComparator() + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (not found) + { + tree := NewWithStringComparator() + tree.Push("xx") + tree.Push("yy") + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + } + + // PrevTo (found) + { + tree := NewWithStringComparator() + tree.Push("aa") + tree.Push("bb") + tree.Push("cc") + it := tree.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty list") + } + if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestBinaryHeapSerialization(t *testing.T) { heap := NewWithStringComparator() diff --git a/trees/binaryheap/iterator.go b/trees/binaryheap/iterator.go index beeb8d7..343fa52 100644 --- a/trees/binaryheap/iterator.go +++ b/trees/binaryheap/iterator.go @@ -82,3 +82,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool { + for iterator.Next() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool { + for iterator.Prev() { + index, value := iterator.Index(), iterator.Value() + if f(index, value) { + return true + } + } + return false +} diff --git a/trees/btree/btree_test.go b/trees/btree/btree_test.go index 4d69665..f4edaf1 100644 --- a/trees/btree/btree_test.go +++ b/trees/btree/btree_test.go @@ -6,6 +6,7 @@ package btree import ( "fmt" + "strings" "testing" ) @@ -1074,6 +1075,112 @@ func assertValidTreeNode(t *testing.T, node *Node, expectedEntries int, expected } } +func TestBTreeIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + tree := NewWithIntComparator(3) + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (not found) + { + tree := NewWithIntComparator(3) + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (found) + { + tree := NewWithIntComparator(3) + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestBTreeIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + tree := NewWithIntComparator(3) + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (not found) + { + tree := NewWithIntComparator(3) + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (found) + { + tree := NewWithIntComparator(3) + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestBTreeSerialization(t *testing.T) { tree := NewWithStringComparator(3) tree.Put("c", "3") diff --git a/trees/btree/iterator.go b/trees/btree/iterator.go index 840db68..ed0cb15 100644 --- a/trees/btree/iterator.go +++ b/trees/btree/iterator.go @@ -191,3 +191,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/trees/redblacktree/iterator.go b/trees/redblacktree/iterator.go index 9cde5e4..881b67d 100644 --- a/trees/redblacktree/iterator.go +++ b/trees/redblacktree/iterator.go @@ -159,3 +159,31 @@ func (iterator *Iterator) Last() bool { iterator.End() return iterator.Prev() } + +// NextTo moves the iterator to the next element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Next() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} + +// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the +// 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. +func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool { + for iterator.Prev() { + key, value := iterator.Key(), iterator.Value() + if f(key, value) { + return true + } + } + return false +} diff --git a/trees/redblacktree/redblacktree_test.go b/trees/redblacktree/redblacktree_test.go index 74c3d8a..730a1f3 100644 --- a/trees/redblacktree/redblacktree_test.go +++ b/trees/redblacktree/redblacktree_test.go @@ -6,6 +6,7 @@ package redblacktree import ( "fmt" + "strings" "testing" ) @@ -557,6 +558,112 @@ func TestRedBlackTreeIteratorLast(t *testing.T) { } } +func TestRedBlackTreeIteratorNextTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // NextTo (empty) + { + tree := NewWithIntComparator() + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (not found) + { + tree := NewWithIntComparator() + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + for it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // NextTo (found) + { + tree := NewWithIntComparator() + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.Begin() + if !it.NextTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Next() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 2 || value.(string) != "cc" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc") + } + if it.Next() { + t.Errorf("Should not go past last element") + } + } +} + +func TestRedBlackTreeIteratorPrevTo(t *testing.T) { + // Sample seek function, i.e. string starting with "b" + seek := func(index interface{}, value interface{}) bool { + return strings.HasSuffix(value.(string), "b") + } + + // PrevTo (empty) + { + tree := NewWithIntComparator() + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (not found) + { + tree := NewWithIntComparator() + tree.Put(0, "xx") + tree.Put(1, "yy") + it := tree.Iterator() + it.End() + for it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + } + + // PrevTo (found) + { + tree := NewWithIntComparator() + tree.Put(2, "cc") + tree.Put(0, "aa") + tree.Put(1, "bb") + it := tree.Iterator() + it.End() + if !it.PrevTo(seek) { + t.Errorf("Shouldn't iterate on empty tree") + } + if index, value := it.Key(), it.Value(); index != 1 || value.(string) != "bb" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb") + } + if !it.Prev() { + t.Errorf("Should go to first element") + } + if index, value := it.Key(), it.Value(); index != 0 || value.(string) != "aa" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa") + } + if it.Prev() { + t.Errorf("Should not go before first element") + } + } +} + func TestRedBlackTreeSerialization(t *testing.T) { tree := NewWithStringComparator() tree.Put("c", "3")