diff --git a/README.md b/README.md index 1160c1b..f3633cd 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,8 @@ From(sentences). ## Release Notes ~~~ +v3.0.2 (2020-02-23) +* Added FromChannelT(). v3.0.1 (2019-07-09) * Support for Go modules diff --git a/benchmark_test.go b/benchmark_test.go index 55eb296..e27d61e 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -61,3 +61,33 @@ func BenchmarkZipSkipTake_generics(b *testing.B) { }).Skip(2).Take(5) } } + +func BenchmarkFromChannel(b *testing.B) { + for n := 0; n < b.N; n++ { + ch := make(chan interface{}) + go func() { + for i := 0; i < size; i++ { + ch <- i + } + + close(ch) + }() + + FromChannel(ch).All(func(i interface{}) bool { return true }) + } +} + +func BenchmarkFromChannelT(b *testing.B) { + for n := 0; n < b.N; n++ { + ch := make(chan interface{}) + go func() { + for i := 0; i < size; i++ { + ch <- i + } + + close(ch) + }() + + FromChannelT(ch).All(func(i interface{}) bool { return true }) + } +} diff --git a/example_test.go b/example_test.go index 4014247..a46af5b 100644 --- a/example_test.go +++ b/example_test.go @@ -2509,7 +2509,7 @@ func ExampleQuery_WhereIndexedT() { // [0 20 15 40] } -// The following code example demonstrates how to use the Zip +// The following code example demonstrates how to use the ZipT // method to merge two slices. func ExampleQuery_ZipT() { number := []int{1, 2, 3, 4, 5} @@ -2524,3 +2524,19 @@ func ExampleQuery_ZipT() { // Output: // [[1 one] [2 two] [3 three]] } + +// The following code example demonstrates how to use the FromChannelT +// to make a Query from typed channel. +func ExampleFromChannelT() { + ch := make(chan string, 3) + ch <- "one" + ch <- "two" + ch <- "three" + close(ch) + + q := FromChannelT(ch) + + fmt.Println(q.Results()) + // Output: + // [one two three] +} diff --git a/from.go b/from.go index 21ee9f4..e1c1867 100644 --- a/from.go +++ b/from.go @@ -78,7 +78,11 @@ func From(source interface{}) Query { case reflect.String: return FromString(source.(string)) case reflect.Chan: - return FromChannel(source.(chan interface{})) + if _, ok := source.(chan interface{}); ok { + return FromChannel(source.(chan interface{})) + } else { + return FromChannelT(source) + } default: return FromIterable(source.(Iterable)) } @@ -97,6 +101,23 @@ func FromChannel(source <-chan interface{}) Query { } } +// FromChannelT is the typed version of FromChannel. +// +// - source is of type "chan TSource" +// +// NOTE: FromChannel has better performance than FromChannelT. +func FromChannelT(source interface{}) Query { + src := reflect.ValueOf(source) + return Query{ + Iterate: func() Iterator { + return func() (interface{}, bool) { + value, ok := src.Recv() + return value.Interface(), ok + } + }, + } +} + // FromString initializes a linq query with passed string, linq iterates over // runes of string. func FromString(source string) Query { diff --git a/from_test.go b/from_test.go index 9dc4e23..dbc469d 100644 --- a/from_test.go +++ b/from_test.go @@ -9,6 +9,12 @@ func TestFrom(t *testing.T) { c <- 1 close(c) + ct := make(chan int, 3) + ct <- -10 + ct <- 0 + ct <- 10 + close(ct) + tests := []struct { input interface{} output []interface{} @@ -23,6 +29,7 @@ func TestFrom(t *testing.T) { {map[string]bool{"foo": true}, []interface{}{KeyValue{"foo", true}}, true}, {map[string]bool{"foo": true}, []interface{}{KeyValue{"foo", false}}, false}, {c, []interface{}{-1, 0, 1}, true}, + {ct, []interface{}{-10, 0, 10}, true}, {foo{f1: 1, f2: true, f3: "string"}, []interface{}{1, true, "string"}, true}, } @@ -51,6 +58,20 @@ func TestFromChannel(t *testing.T) { } } +func TestFromChannelT(t *testing.T) { + c := make(chan int, 3) + c <- 10 + c <- 15 + c <- -3 + close(c) + + w := []interface{}{10, 15, -3} + + if q := FromChannelT(c); !validateQuery(q, w) { + t.Errorf("FromChannelT() failed expected %v", w) + } +} + func TestFromString(t *testing.T) { s := "string" w := []interface{}{'s', 't', 'r', 'i', 'n', 'g'}