Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#67 Empty chaining #69

Merged
merged 6 commits into from
Jan 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,14 @@ func testBeforeAndAfter[T any](t *testing.T, itb gcf.Iterable[T]) {
assert.Zero(it.Current())
})
}

// testEmptyChain tests Iterable is emptyIterable when chained from emptyIterable.
// On function called with emptyIterable should be emptyIterable.
func testEmptyChain(t *testing.T, f func(itb gcf.Iterable[int]) gcf.Iterable[int]) {
meian marked this conversation as resolved.
Show resolved Hide resolved
t.Helper()
itbe := gcf.FromSlice([]int{})
t.Run("is empty Iterable", func(t *testing.T) {
itb := f(itbe)
assert.True(t, gcf.IsEmptyIterable(itb), "%v", gcf.ToSlice(itb))
})
}
6 changes: 3 additions & 3 deletions concat.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ type concatIterator[T any] struct {
// itb2 := gcf.FromSlice([]int{4, 5, 6})
// itbc := gcf.Concat(itb1, itb2)
func Concat[T any](itb1 Iterable[T], itb2 Iterable[T]) Iterable[T] {
if itb1 == nil && itb2 == nil {
if isEmpty(itb1) && isEmpty(itb2) {
return empty[T]()
}
if itb2 == nil {
if isEmpty(itb2) {
return itb1
}
if itb1 == nil {
if isEmpty(itb1) {
return itb2
}
return &concatIterable[T]{itb1, itb2}
Expand Down
7 changes: 7 additions & 0 deletions concat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ func TestConcat(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3})
itb = gcf.Concat(itb, gcf.FromSlice([]int{4, 5, 6}))
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
meian marked this conversation as resolved.
Show resolved Hide resolved
return gcf.Concat(itb, gcf.FromSlice([]int{}))
})
testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Concat(itb, nil)
})
}

func ExampleConcat() {
Expand Down
4 changes: 2 additions & 2 deletions distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type distinctIterator[T comparable] struct {
//
// Currently, result order is determined, but on spec, is undefined.
func Distinct[T comparable](itb Iterable[T]) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
// Because no change has with Distinct combined Distinct, original Iterable is returned
if itbd, ok := itb.(*distinctIterable[T]); ok {
Expand Down
4 changes: 4 additions & 0 deletions distinct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func TestDistinct(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3, 2, 4})
itb = gcf.Distinct(itb)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Distinct(itb)
})
}

func ExampleDistinct() {
Expand Down
15 changes: 15 additions & 0 deletions empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ func empty[T any]() Iterable[T] {
return emptyIterable[T]{}
}

func orEmpty[T any](itb Iterable[T]) Iterable[T] {
if itb == nil {
return empty[T]()
}
return itb
}

func isEmpty[T any](itb Iterable[T]) bool {
if itb == nil {
return true
}
_, ok := itb.(emptyIterable[T])
return ok
}

func (itb emptyIterable[T]) Iterator() Iterator[T] {
return emptyIter[T]()
}
Expand Down
6 changes: 6 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package gcf

// IsEmptyIterable detects itb is emptyIterable.
func IsEmptyIterable[T any](itb Iterable[T]) bool {
return isEmpty(itb)
}
4 changes: 2 additions & 2 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type filterIterator[T any] struct {
//
// If filterFunc is nil, returns original Iteratable.
func Filter[T any](itb Iterable[T], filterFunc func(v T) bool) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
if filterFunc == nil {
return itb
Expand Down
4 changes: 4 additions & 0 deletions filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func TestFilter(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3, 4})
itb = gcf.Filter(itb, func(v int) bool { return v%2 == 0 })
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Filter(itb, func(v int) bool { return true })
})
}

func ExampleFilter() {
Expand Down
4 changes: 2 additions & 2 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type mapIterator[T any, R any] struct {
//
// If mapFunc is nil, return Iterable in zero value elements.
func Map[T any, R any](itb Iterable[T], mapFunc func(v T) R) Iterable[R] {
if itb == nil {
if isEmpty(itb) {
return empty[R]()
}
if mapFunc == nil {
Expand Down Expand Up @@ -69,7 +69,7 @@ type flatMapIterator[T any, R any] struct {
//
// If mapFunc is nil, return empty Iterable.
func FlatMap[T any, R any](itb Iterable[T], mapFunc func(v T) []R) Iterable[R] {
if itb == nil {
if isEmpty(itb) {
return empty[R]()
}
if mapFunc == nil {
Expand Down
12 changes: 10 additions & 2 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func TestMap(t *testing.T) {
assert.Equal(tt.want, s)
})
}

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Map(itb, func(v int) int { return v })
})
}

func ExampleMap() {
Expand Down Expand Up @@ -136,10 +140,14 @@ func TestFlatMap(t *testing.T) {
}

itb := gcf.FromSlice([]int{1, 2, 3, 4})
itbs := gcf.Map(itb, func(v int) string {
return strings.Repeat("a", v)
itbs := gcf.FlatMap(itb, func(v int) []string {
return []string{strings.Repeat("a", v)}
meian marked this conversation as resolved.
Show resolved Hide resolved
})
testBeforeAndAfter(t, itbs)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.FlatMap(itb, func(v int) []int { return []int{v, v, v} })
})
}

func ExampleFlatMap() {
Expand Down
6 changes: 6 additions & 0 deletions range.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ func Range[T constraints.Integer](start, end, step T) (Iterable[T], error) {
if start == end {
return FromSlice([]T{start}), nil
}
if step > 0 && start > end {
return empty[T](), nil
}
if step < 0 && start < end {
return empty[T](), nil
}
meian marked this conversation as resolved.
Show resolved Hide resolved
return &rangeIterable[T]{start, end, step}, nil
}

Expand Down
9 changes: 9 additions & 0 deletions range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ func TestRange(t *testing.T) {
testBeforeAndAfter(t, itb)
itb, _ = gcf.Range(3, 1, -1)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
itb, _ = gcf.Range(1, 0, 1)
return itb
})
testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
itb, _ = gcf.Range(1, 2, -1)
return itb
})
meian marked this conversation as resolved.
Show resolved Hide resolved
}

func ExampleRange() {
Expand Down
4 changes: 2 additions & 2 deletions repeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ type repeatIterableIterator[T any] struct {
//
// If count is 0 or negative, return Iterable with no element.
func RepeatIterable[T any](itb Iterable[T], count int) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
if count < 1 {
return empty[T]()
Expand Down
14 changes: 14 additions & 0 deletions repeat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ func TestRepeat(t *testing.T) {

itb := gcf.Repeat(1, 3)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Repeat(1, 0)
})
testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Repeat(1, -1)
})
meian marked this conversation as resolved.
Show resolved Hide resolved
}

func ExampleRepeat() {
Expand Down Expand Up @@ -116,6 +123,13 @@ func TestRepeatIterable(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3})
itb = gcf.RepeatIterable(itb, 2)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.RepeatIterable(itb, 0)
})
testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.RepeatIterable(itb, -1)
})
}

func ExampleRepeatIterable() {
Expand Down
4 changes: 2 additions & 2 deletions reverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ type reverseIterator[T any] struct {
// itb := gcf.FromSlice([]int{1, 2, 3})
// itb = gcf.Reverse(itb)
func Reverse[T any](itb Iterable[T]) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
// reverse in reverse is original Iterable.
if itbr, ok := itb.(*reverseIterable[T]); ok {
Expand Down
4 changes: 4 additions & 0 deletions reverse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func TestReverse(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3})
itb = gcf.Reverse(itb)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Reverse(itb)
})
}

func ExampleReverse() {
Expand Down
8 changes: 4 additions & 4 deletions skip.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type skipIterator[T any] struct {
//
// If count is 0 or negative, returns original Iterable.
func Skip[T any](itb Iterable[T], count int) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
if count < 1 {
return itb
Expand Down Expand Up @@ -67,8 +67,8 @@ type skipWhileIterator[T any] struct {
//
// If whileFunc is nil, returns original Iterable.
func SkipWhile[T any](itb Iterable[T], whileFunc func(v T) bool) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
if whileFunc == nil {
return itb
Expand Down
8 changes: 8 additions & 0 deletions skip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ func TestSkip(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3})
itb = gcf.Skip(itb, 2)
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.Skip(itb, 0)
})
}

func ExampleSkip() {
Expand Down Expand Up @@ -166,6 +170,10 @@ func TestSkipWhile(t *testing.T) {
itb := gcf.FromSlice([]int{1, 2, 3})
itb = gcf.SkipWhile(itb, func(v int) bool { return v < 2 })
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.SkipWhile(itb, func(v int) bool { return true })
})
}

func ExampleSkipWhile() {
Expand Down
8 changes: 8 additions & 0 deletions slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ func TestFromSlice_pointer(t *testing.T) {

itb := gcf.FromSlice([]*int{&i1, &i2, &i3})
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.FromSlice([]int{})
})
meian marked this conversation as resolved.
Show resolved Hide resolved
}

func TestFromSliceImmutable(t *testing.T) {
Expand Down Expand Up @@ -142,6 +146,10 @@ func TestFromSliceImmutable(t *testing.T) {

itb := gcf.FromSliceImmutable([]int{1, 2, 3})
testBeforeAndAfter(t, itb)

testEmptyChain(t, func(itb gcf.Iterable[int]) gcf.Iterable[int] {
return gcf.FromSliceImmutable([]int{})
})
meian marked this conversation as resolved.
Show resolved Hide resolved
}

func TestToSlice(t *testing.T) {
Expand Down
12 changes: 6 additions & 6 deletions sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func (s funcSorter[T]) Sort() { sort.Sort(s) }
// itb := gcf.FromSlice([]int{1, 3, 2})
// itb = gcf.SortAsc(itb)
func SortAsc[T constraints.Ordered](itb Iterable[T]) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
toSorter := func(s []T) sorter[T] { return ascSlice[T](s) }
return &sortIterable[T]{itb, toSorter}
Expand All @@ -72,8 +72,8 @@ func SortAsc[T constraints.Ordered](itb Iterable[T]) Iterable[T] {
// itb := gcf.FromSlice([]int{1, 3, 2})
// itb = gcf.SortDesc(itb)
func SortDesc[T constraints.Ordered](itb Iterable[T]) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
toSorter := func(s []T) sorter[T] { return descSlice[T](s) }
return &sortIterable[T]{itb, toSorter}
Expand All @@ -87,8 +87,8 @@ func SortDesc[T constraints.Ordered](itb Iterable[T]) Iterable[T] {
//
// The less function takes x element and y element, and returns true if x is less than y.
func SortBy[T any](itb Iterable[T], less func(x, y T) bool) Iterable[T] {
if itb == nil {
return empty[T]()
if isEmpty(itb) {
return orEmpty(itb)
}
toSorter := func(s []T) sorter[T] { return funcSorter[T]{s, less} }
return &sortIterable[T]{itb, toSorter}
Expand Down
Loading