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

Add Generics to go-linq #112

Open
wants to merge 56 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
6232ae2
add QueryG type, add FromSliceG and ToSliceG method
lonegunmanb Aug 5, 2022
860409a
Add SelectG method
lonegunmanb Aug 6, 2022
bb014ff
Add SelectIndexedG method
lonegunmanb Aug 6, 2022
8db3799
Add aggregate methods
lonegunmanb Aug 6, 2022
2abcc65
Add concat methods
lonegunmanb Aug 6, 2022
8f9ec6a
Add RepeatG method
lonegunmanb Aug 6, 2022
cd3fb5d
Add DefaultIfEmptyG method
lonegunmanb Aug 6, 2022
ad27354
Add FromMapG function
lonegunmanb Aug 6, 2022
28ff320
Add FromStringG function
lonegunmanb Aug 6, 2022
5f62989
Add FromChannelG function
lonegunmanb Aug 6, 2022
63807a4
Add FromIterableG function
lonegunmanb Aug 6, 2022
b38b9c4
Add RangeG function
lonegunmanb Aug 6, 2022
bfe98ce
Add ToMapG method
lonegunmanb Aug 6, 2022
d6415a5
Add AllG method
lonegunmanb Aug 6, 2022
83846ff
Add AnyG method
lonegunmanb Aug 6, 2022
a78b1f6
Add AnyWithG method
lonegunmanb Aug 6, 2022
57cd3e1
Add ContainsG method
lonegunmanb Aug 6, 2022
98d1c47
Add AverageG method
lonegunmanb Aug 6, 2022
ee76d8b
Add CountG method
lonegunmanb Aug 6, 2022
4d55482
Add CountWithG method
lonegunmanb Aug 6, 2022
8ded08d
Add FirstG method
lonegunmanb Aug 6, 2022
7e4d200
Add FirstWithG
lonegunmanb Aug 6, 2022
f3eb232
Add ForEachG
lonegunmanb Aug 6, 2022
1cda6e9
Add ForEachIndexedG method
lonegunmanb Aug 6, 2022
303de64
Add LastG method
lonegunmanb Aug 6, 2022
53ca402
Change FirstG method return list
lonegunmanb Aug 6, 2022
823d94d
Add LastWithG method
lonegunmanb Aug 6, 2022
36c793a
Add MaxG and MinG method
lonegunmanb Aug 6, 2022
987d87a
Add SequenceEqualG method
lonegunmanb Aug 6, 2022
96d66ff
Add SingleWithG method
lonegunmanb Aug 6, 2022
c4739a8
Add ZipG method
lonegunmanb Aug 6, 2022
9456e49
Add WhereIndexdG method
lonegunmanb Aug 6, 2022
978efbc
Add UnionG method
lonegunmanb Aug 6, 2022
7c765d9
Add SumG method
lonegunmanb Aug 6, 2022
b23b0c1
Add ToChannelG method
lonegunmanb Aug 6, 2022
10256e3
Add test case to ToChannelG method
lonegunmanb Aug 6, 2022
949571d
Add IndexOfG method
lonegunmanb Aug 6, 2022
faa27c2
Add IntersectG method
lonegunmanb Aug 6, 2022
a9842bf
Add Expender to expend type parameters for method
lonegunmanb Aug 7, 2022
5084caf
Add SelectMany methods
lonegunmanb Aug 7, 2022
d9da2de
Add SkipG methods
lonegunmanb Aug 7, 2022
73b4056
Add SkipWhileG methods
lonegunmanb Aug 7, 2022
87f11f9
Add SkipWhileIndexedG methods
lonegunmanb Aug 7, 2022
289fae2
Add TakeG methods
lonegunmanb Aug 7, 2022
1e6e9cd
Add DistinctG methods(No ordered query)
lonegunmanb Aug 7, 2022
697df8b
Add OrderedQueryG methods
lonegunmanb Aug 7, 2022
781bedf
Add ExceptG methods
lonegunmanb Aug 7, 2022
2eeb113
Add Channel2ChannelG test
lonegunmanb Aug 7, 2022
2f17257
Add GroupByG test
lonegunmanb Aug 7, 2022
cdc9651
Add JoinG test
lonegunmanb Aug 7, 2022
4943821
Add ReverseG method
lonegunmanb Aug 7, 2022
5c2ecfb
Add GroupJoinG method
lonegunmanb Aug 7, 2022
997cfb6
Rename `enpender.go` to `expander.go`
lonegunmanb Aug 7, 2022
28c4665
improve Expander type safe
lonegunmanb Aug 7, 2022
214febd
remove code in comment
lonegunmanb Aug 7, 2022
e8bd199
Remove unused Select function.
lonegunmanb Aug 8, 2022
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
30 changes: 30 additions & 0 deletions aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ func (q Query) AggregateT(f interface{}) interface{} {
return q.Aggregate(fFunc)
}

func (q QueryG[T]) Aggregate(f func(T, T) T) T {
next := q.Iterate()

result, any := next()
if !any {
return *new(T)
}

for current, ok := next(); ok; current, ok = next() {
result = f(result, current)
}

return result
}

// AggregateWithSeed applies an accumulator function over a sequence. The
// specified seed value is used as the initial accumulator value.
//
Expand Down Expand Up @@ -93,6 +108,17 @@ func (q Query) AggregateWithSeedT(seed interface{},
return q.AggregateWithSeed(seed, fFunc)
}

func (q QueryG[T]) AggregateWithSeed(seed T, f func(T, T) T) T {
next := q.Iterate()
result := seed

for current, ok := next(); ok; current, ok = next() {
result = f(result, current)
}

return result
}

// AggregateWithSeedBy applies an accumulator function over a sequence. The
// specified seed value is used as the initial accumulator value, and the
// specified function is used to select the result value.
Expand Down Expand Up @@ -156,3 +182,7 @@ func (q Query) AggregateWithSeedByT(seed interface{},

return q.AggregateWithSeedBy(seed, fFunc, resultSelectorFunc)
}

func (q QueryG[T]) AggregateWithSeedBy(seed T, f func(T, T) T, resultSelector func(T) interface{}) interface{} {
return resultSelector(q.AggregateWithSeed(seed, f))
}
60 changes: 59 additions & 1 deletion aggregate_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package linq

import "testing"
import (
"github.com/stretchr/testify/assert"
"testing"
)
import "strings"

func TestAggregate(t *testing.T) {
Expand All @@ -26,6 +29,42 @@ func TestAggregate(t *testing.T) {
}
}

func TestAggregateG(t *testing.T) {
input := []string{"apple", "mango", "orange", "passionfruit", "grape"}
expected := "passionfruit"
actual := FromSliceG(input).Aggregate(func(r, i string) string {
if len(r) > len(i) {
return r
}
return i
})
assert.Equal(t, expected, actual)
}

func TestAggregateSumG(t *testing.T) {
input := []int{1, 2, 3, 4}
expected := 10
actual := FromSliceG(input).Aggregate(func(i1, i2 int) int {
return i1 + i2
})
assert.Equal(t, expected, actual)
}

func TestAggregateWithSeedG(t *testing.T) {
input := []int{1, 2, 3, 4}
expected := 15
actual := FromSliceG(input).AggregateWithSeed(5, func(i1, i2 int) int {
return i1 + i2
})
assert.Equal(t, expected, actual)
input = []int{}
expected = 5
actual = FromSliceG(input).AggregateWithSeed(5, func(i1, i2 int) int {
return i1 + i2
})
assert.Equal(t, expected, actual)
}

func TestAggregateT_PanicWhenFunctionIsInvalid(t *testing.T) {
mustPanicWithError(t, "AggregateT: parameter [f] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,string,string)string'", func() {
From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AggregateT(func(x int, r string, i string) string {
Expand Down Expand Up @@ -117,3 +156,22 @@ func TestAggregateWithSeedByT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) {
)
})
}

func TestAggregateWithSeedByG(t *testing.T) {
input := []string{"apple", "mango", "orange", "passionfruit", "grape"}
expected := "PASSIONFRUIT"

actual := FromSliceG(input).AggregateWithSeedBy("banana",
func(r, i string) string {
if len(r) > len(i) {
return r
}
return i
},
func(r string) interface{} {
return strings.ToUpper(r)
},
).(string)

assert.Equal(t, expected, actual)
}
64 changes: 64 additions & 0 deletions concat.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ func (q Query) Append(item interface{}) Query {
}
}

func (q QueryG[T]) Append(item T) QueryG[T] {
return QueryG[T]{
Iterate: func() IteratorG[T] {
next := q.Iterate()
appended := false

return func() (T, bool) {
i, ok := next()
if ok {
return i, ok
}

if !appended {
appended = true
return item, true
}

return *new(T), false
}
},
}
}

// Concat concatenates two collections.
//
// The Concat method differs from the Union method because the Concat method
Expand Down Expand Up @@ -53,6 +76,29 @@ func (q Query) Concat(q2 Query) Query {
}
}

func (q QueryG[T]) Concat(q2 QueryG[T]) QueryG[T] {
return QueryG[T]{
Iterate: func() IteratorG[T] {
next := q.Iterate()
next2 := q2.Iterate()
use1 := true

return func() (item T, ok bool) {
if use1 {
item, ok = next()
if ok {
return
}

use1 = false
}

return next2()
}
},
}
}

// Prepend inserts an item to the beginning of a collection, so it becomes the
// first item.
func (q Query) Prepend(item interface{}) Query {
Expand All @@ -72,3 +118,21 @@ func (q Query) Prepend(item interface{}) Query {
},
}
}

func (q QueryG[T]) Prepend(item T) QueryG[T] {
return QueryG[T]{
Iterate: func() IteratorG[T] {
next := q.Iterate()
prepended := false

return func() (T, bool) {
if prepended {
return next()
}

prepended = true
return item, true
}
},
}
}
27 changes: 26 additions & 1 deletion concat_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package linq

import "testing"
import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestAppend(t *testing.T) {
input := []int{1, 2, 3, 4}
Expand All @@ -11,6 +14,13 @@ func TestAppend(t *testing.T) {
}
}

func TestAppendG(t *testing.T) {
input := []int{1, 2, 3, 4}
expected := []int{1, 2, 3, 4, 5}
actual := FromSliceG(input).Append(5).ToSlice()
assert.Equal(t, expected, actual)
}

func TestConcat(t *testing.T) {
input1 := []int{1, 2, 3}
input2 := []int{4, 5}
Expand All @@ -21,6 +31,14 @@ func TestConcat(t *testing.T) {
}
}

func TestConcatG(t *testing.T) {
input1 := []int{1, 2, 3}
input2 := []int{4, 5}
expected := []int{1, 2, 3, 4, 5}
actual := FromSliceG(input1).Concat(FromSliceG(input2)).ToSlice()
assert.Equal(t, expected, actual)
}

func TestPrepend(t *testing.T) {
input := []int{1, 2, 3, 4}
want := []interface{}{0, 1, 2, 3, 4}
Expand All @@ -29,3 +47,10 @@ func TestPrepend(t *testing.T) {
t.Errorf("From(%v).Prepend()=%v expected %v", input, toSlice(q), want)
}
}

func TestPrependG(t *testing.T) {
input := []int{1, 2, 3, 4}
want := []int{0, 1, 2, 3, 4}
actual := FromSliceG(input).Prepend(0).ToSlice()
assert.Equal(t, want, actual)
}
30 changes: 30 additions & 0 deletions defaultifempty.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,33 @@ func (q Query) DefaultIfEmpty(defaultValue interface{}) Query {
},
}
}

func (q QueryG[T]) DefaultIfEmpty(defaultValue T) QueryG[T] {
return QueryG[T]{
Iterate: func() IteratorG[T] {
next := q.Iterate()
state := 1

return func() (item T, ok bool) {
switch state {
case 1:
item, ok = next()
if ok {
state = 2
} else {
item = defaultValue
ok = true
state = -1
}
return
case 2:
for item, ok = next(); ok; item, ok = next() {
return
}
return
}
return
}
},
}
}
19 changes: 19 additions & 0 deletions defaultifempty_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package linq

import (
"github.com/stretchr/testify/assert"
"testing"
)

Expand All @@ -23,3 +24,21 @@ func TestDefaultIfEmpty(t *testing.T) {
}

}

func TestDefaultIfEmptyG(t *testing.T) {
defaultValue := 0
tests := []struct {
input []int
want []int
}{
{[]int{}, []int{defaultValue}},
{[]int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}},
}

for _, test := range tests {
actual := FromSliceG(test.input).DefaultIfEmpty(defaultValue).ToSlice()

assert.Equal(t, test.want, actual)
}

}
14 changes: 14 additions & 0 deletions distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func (q Query) Distinct() Query {
}
}

func (q QueryG[T]) Distinct() QueryG[T] {
return AsQueryG[T](q.AsQuery().Distinct())
}

// Distinct method returns distinct elements from a collection. The result is an
// ordered collection that contains no duplicate values.
//
Expand Down Expand Up @@ -50,6 +54,10 @@ func (oq OrderedQuery) Distinct() OrderedQuery {
}
}

func (q OrderedQueryG[T]) Distinct() OrderedQueryG[T] {
return asOrderQueryG[T](q.orderedQuery.Distinct())
}

// DistinctBy method returns distinct elements from a collection. This method
// executes selector function for each element to determine a value to compare.
// The result is an unordered collection that contains no duplicate values.
Expand All @@ -74,6 +82,12 @@ func (q Query) DistinctBy(selector func(interface{}) interface{}) Query {
}
}

func (e *Expended[T1, T2]) DistinctBy(selector func(T1) T2) QueryG[T1] {
return AsQueryG[T1](e.q.AsQuery().DistinctBy(func(i interface{}) interface{} {
return selector(i.(T1))
}))
}

// DistinctByT is the typed version of DistinctBy.
//
// - selectorFn is of type "func(TSource) TSource".
Expand Down
Loading