diff --git a/README.md b/README.md index 2de4fc9..14fc1f8 100644 --- a/README.md +++ b/README.md @@ -177,3 +177,4 @@ problems from * [Day 163](https://github.com/vaskoz/dailycodingproblem-go/issues/336) * [Day 164](https://github.com/vaskoz/dailycodingproblem-go/issues/340) * [Day 165](https://github.com/vaskoz/dailycodingproblem-go/issues/343) +* [Day 166](https://github.com/vaskoz/dailycodingproblem-go/issues/346) diff --git a/day166/problem.go b/day166/problem.go new file mode 100644 index 0000000..b9be536 --- /dev/null +++ b/day166/problem.go @@ -0,0 +1,78 @@ +package day166 + +import ( + "errors" +) + +var errNoMoreElements = errors.New("no more elements to iterate") + +// ErrNoMoreElements is returned when there are no more elements to iterate. +func ErrNoMoreElements() error { + return errNoMoreElements +} + +// TwoDimensionalIterator can iterate over a slice of slices of anything. +type TwoDimensionalIterator interface { + Next() (interface{}, error) + HasNext() bool +} + +type twoDimensionalIterator struct { + sliceIndex, positionInSlice int + finished bool + data [][]interface{} +} + +// NewTwoDimensionalIterator returns a new instance of TwoDimensionalIterator. +func NewTwoDimensionalIterator(data [][]interface{}) TwoDimensionalIterator { + finished := true + var sliceIndex, positionInSlice int + for i := range data { + for j := range data[i] { + finished = false + sliceIndex = i + positionInSlice = j + break + } + if !finished { + break + } + } + return &twoDimensionalIterator{ + sliceIndex: sliceIndex, finished: finished, positionInSlice: positionInSlice, data: data, + } +} + +func (tdi *twoDimensionalIterator) Next() (interface{}, error) { + if tdi.finished { + return nil, ErrNoMoreElements() + } + sliceIndex := -1 + positionIndex := 0 + for i := tdi.sliceIndex; i < len(tdi.data); i++ { + positionIndex = 0 + if i == tdi.sliceIndex { + positionIndex = tdi.positionInSlice + 1 + } + for j := positionIndex; j < len(tdi.data[i]); j++ { + sliceIndex = i + positionIndex = j + break + } + if sliceIndex != -1 { + break + } + } + result := tdi.data[tdi.sliceIndex][tdi.positionInSlice] + if sliceIndex == -1 { + tdi.finished = true + } else { + tdi.sliceIndex = sliceIndex + tdi.positionInSlice = positionIndex + } + return result, nil +} + +func (tdi *twoDimensionalIterator) HasNext() bool { + return !tdi.finished +} diff --git a/day166/problem_test.go b/day166/problem_test.go new file mode 100644 index 0000000..e5def2a --- /dev/null +++ b/day166/problem_test.go @@ -0,0 +1,49 @@ +package day166 + +import "testing" + +var testcases = []struct { + input [][]interface{} + expected []interface{} +}{ + {[][]interface{}{ + {1, 2}, {3}, {}, {4, 5, 6}, + }, + []interface{}{1, 2, 3, 4, 5, 6}, + }, + {nil, []interface{}{}}, + {[][]interface{}{{}, {}, {}}, []interface{}{}}, +} + +func TestTwoDimensionalIterator(t *testing.T) { + t.Parallel() + for _, tc := range testcases { + tdi := NewTwoDimensionalIterator(tc.input) + for _, v := range tc.expected { + if !tdi.HasNext() { + t.Error("HasNext should return true") + } + if result, err := tdi.Next(); result != v || err != nil { + t.Errorf("Expected (%v,nil) and got (%v,%v)", v, result, err) + } + } + if tdi.HasNext() { + t.Error("HasNext should return false") + } + if _, err := tdi.Next(); err == nil { + t.Errorf("Expected an error on Next when HasNext is false") + } + } +} + +func BenchmarkTwoDimensionalIterator(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tc := range testcases { + tdi := NewTwoDimensionalIterator(tc.input) + for range tc.expected { + tdi.Next() + tdi.HasNext() + } + } + } +}