Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 166
Good morning! Here's your coding interview problem for today.

This problem was asked by Uber.

Implement a 2D iterator class. It will be initialized with an array of arrays, and should implement the following methods:

* `next()`: returns the next element in the array of arrays. If there are no more elements, raise an exception.
* `has_next()`: returns whether or not the iterator still has elements left.
For example, given the input `[[1, 2], [3], [], [4, 5, 6]]`, calling `next()` repeatedly should output `1, 2, 3, 4, 5, 6`.

Do not use flatten or otherwise clone the arrays. Some of the arrays can be empty.

Pull request:
Day166
Resolves #346.

Patch:
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
+}


Test patch:
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()
+			}
+		}
+	}
+}