Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 139
Good morning! Here's your coding interview problem for today.

This problem was asked by Google.

Given an iterator with methods next() and hasNext(), create a wrapper iterator, PeekableInterface, which also implements peek(). peek shows the next element that would be returned on next().

Here is the interface:
```
class PeekableInterface(object):
    def __init__(self, iterator):
        pass

    def peek(self):
        pass

    def next(self):
        pass

    def hasNext(self):
        pass
```

Pull request:
Day139
Resolves #288.

Patch:
diff --git a/README.md b/README.md
index f23e4d4..66773eb 100644
--- a/README.md
+++ b/README.md
@@ -146,3 +146,4 @@ problems from
 * [Day 135](https://github.com/vaskoz/dailycodingproblem-go/issues/280)
 * [Day 136](https://github.com/vaskoz/dailycodingproblem-go/issues/282)
 * [Day 137](https://github.com/vaskoz/dailycodingproblem-go/issues/285)
+* [Day 139](https://github.com/vaskoz/dailycodingproblem-go/issues/288)
diff --git a/day139/problem.go b/day139/problem.go
new file mode 100644
index 0000000..7149af8
--- /dev/null
+++ b/day139/problem.go
@@ -0,0 +1,49 @@
+package day139
+
+// Iterator is a simple iterator interface.
+type Iterator interface {
+	Next() interface{}
+	HasNext() bool
+}
+
+// PeekableIterator wraps an iterator and provides the ability to peek.
+type PeekableIterator struct {
+	peek   interface{}
+	peeked bool
+	Iter   Iterator
+}
+
+// Peek returns the next element without moving the iterator.
+func (p *PeekableIterator) Peek() interface{} {
+	has := p.Iter.HasNext()
+	if !p.peeked && !has {
+		return nil
+	} else if !p.peeked {
+		p.peeked = true
+		p.peek = p.Iter.Next()
+	}
+	return p.peek
+}
+
+// Next returns the next element and advances the iterator.
+func (p *PeekableIterator) Next() interface{} {
+	has := p.Iter.HasNext()
+	var result interface{}
+	if p.peeked {
+		result = p.peek
+	} else if has {
+		result = p.Iter.Next()
+	}
+	p.peeked = false
+	return result
+}
+
+// HasNext returns true if there is more to read from the iterator
+// and false otherwise.
+func (p *PeekableIterator) HasNext() bool {
+	has := p.Iter.HasNext()
+	if has || p.peeked {
+		return true
+	}
+	return false
+}


Test patch:
diff --git a/day139/problem_test.go b/day139/problem_test.go
new file mode 100644
index 0000000..e354b4e
--- /dev/null
+++ b/day139/problem_test.go
@@ -0,0 +1,89 @@
+package day139
+
+import "testing"
+
+var testcases = []struct {
+	data []interface{}
+}{
+	{[]interface{}{3, 5, 6, 7, 9}},
+	{[]interface{}{"foo", "bar", "baz"}},
+}
+
+type SliceIterator struct {
+	Slice []interface{}
+	pos   int
+}
+
+func (si *SliceIterator) Next() interface{} {
+	if si.pos >= len(si.Slice) {
+		return nil
+	}
+	result := si.Slice[si.pos]
+	si.pos++
+	return result
+}
+
+func (si *SliceIterator) HasNext() bool {
+	return si.pos < len(si.Slice)
+}
+
+func TestPeekableIterator(t *testing.T) {
+	t.Parallel()
+	for _, tc := range testcases {
+		p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}}
+		for i := range tc.data {
+			if result := p.Peek(); result != tc.data[i] {
+				t.Errorf("Peek should return %v got %v", tc.data[i], result)
+			}
+			if more := p.HasNext(); !more {
+				t.Errorf("There should be more data")
+			}
+			if result := p.Next(); result != tc.data[i] {
+				t.Errorf("Next should return %v got %v", tc.data[i], result)
+			}
+		}
+		if result := p.Peek(); result != nil {
+			t.Errorf("Peek should return %v got %v", nil, result)
+		}
+		if more := p.HasNext(); more {
+			t.Errorf("There should be NO more data")
+		}
+		if result := p.Next(); result != nil {
+			t.Errorf("Next should return %v got %v", nil, result)
+		}
+	}
+}
+
+func TestPeekableIteratorNoPeek(t *testing.T) {
+	t.Parallel()
+	for _, tc := range testcases {
+		p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}}
+		for i := range tc.data {
+			if more := p.HasNext(); !more {
+				t.Errorf("There should be more data")
+			}
+			if result := p.Next(); result != tc.data[i] {
+				t.Errorf("Next should return %v got %v", tc.data[i], result)
+			}
+		}
+		if more := p.HasNext(); more {
+			t.Errorf("There should be NO more data")
+		}
+		if result := p.Next(); result != nil {
+			t.Errorf("Next should return %v got %v", nil, result)
+		}
+	}
+}
+
+func BenchmarkPeekableIterator(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, tc := range testcases {
+			p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}}
+			for range tc.data {
+				p.Peek()
+				p.HasNext()
+				p.Next()
+			}
+		}
+	}
+}