Skip to content

Commit c15623e

Browse files
committed
initial release
1 parent 76840e3 commit c15623e

File tree

175 files changed

+3638
-13810
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+3638
-13810
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021-2023 Oleg Subachev
3+
Copyright (c) 2021-2024 Oleg Subachev
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
# go2linq
22
[![Go Reference](https://pkg.go.dev/badge/github.com/solsw/go2linq.svg)](https://pkg.go.dev/github.com/solsw/go2linq/v3)
33

4-
**go2linq** is Go implementation of .NET's
4+
**go2linq v4** is Go implementation of .NET's
55
[LINQ to Objects](https://learn.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/linq-to-objects).
66
(See also: [Language Integrated Query](https://en.wikipedia.org/wiki/Language_Integrated_Query),
77
[LINQ](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/),
88
[Enumerable Class](https://learn.microsoft.com/dotnet/api/system.linq.enumerable).)
99

10-
**go2linq** was initially inspired by Jon Skeet's [Edulinq series](https://codeblog.jonskeet.uk/category/edulinq/).
10+
**go2linq v4** is based on [iter.Seq](https://go.dev/wiki/RangefuncExperiment), so it requires setting *GOEXPERIMENT=rangefunc* when executing **go** commands.
1111

1212
---
1313

1414
## Installation
1515

1616
```
17-
go get github.com/solsw/go2linq/v3
17+
go get github.com/solsw/go2linq/v4
1818
```
1919

2020
## Examples
2121

2222
Examples of **go2linq** usage are in the `Example...` functions in test files
23-
(see [Examples](https://pkg.go.dev/github.com/solsw/go2linq/v3#pkg-examples)).
23+
(see [Examples](https://pkg.go.dev/github.com/solsw/go2linq/v4#pkg-examples)).
2424

2525
### Quick and easy example:
2626

@@ -30,21 +30,19 @@ package main
3030
import (
3131
"fmt"
3232

33-
"github.com/solsw/go2linq/v3"
33+
"github.com/solsw/go2linq/v4"
3434
)
3535

3636
func main() {
37-
filter := go2linq.WhereMust(
38-
go2linq.NewEnSlice(1, 2, 3, 4, 5, 6, 7, 8),
37+
filter, _ := go2linq.Where(
38+
go2linq.VarAll(1, 2, 3, 4, 5, 6, 7, 8),
3939
func(i int) bool { return i > 6 || i%2 == 0 },
4040
)
41-
squares := go2linq.SelectMust(
41+
squares, _ := go2linq.Select(
4242
filter,
4343
func(i int) string { return fmt.Sprintf("%d: %d", i, i*i) },
4444
)
45-
enr := squares.GetEnumerator()
46-
for enr.MoveNext() {
47-
square := enr.Current()
45+
for square := range squares {
4846
fmt.Println(square)
4947
}
5048
}

aggregate.go

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,64 @@
11
package go2linq
22

33
import (
4-
"github.com/solsw/errorhelper"
4+
"iter"
5+
56
"github.com/solsw/generichelper"
67
)
78

8-
// Reimplementing LINQ to Objects: Part 13 - Aggregate
9-
// https://codeblog.jonskeet.uk/2010/12/30/reimplementing-linq-to-objects-part-13-aggregate/
10-
// https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
11-
129
// [Aggregate] applies an accumulator function over a sequence.
1310
//
1411
// [Aggregate]: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
15-
func Aggregate[Source any](source Enumerable[Source], accumulator func(Source, Source) Source) (Source, error) {
12+
func Aggregate[Source any](source iter.Seq[Source], accumulator func(Source, Source) Source) (Source, error) {
1613
if source == nil {
1714
return generichelper.ZeroValue[Source](), ErrNilSource
1815
}
1916
if accumulator == nil {
2017
return generichelper.ZeroValue[Source](), ErrNilAccumulator
2118
}
22-
enr := source.GetEnumerator()
23-
if !enr.MoveNext() {
24-
return generichelper.ZeroValue[Source](), ErrEmptySource
19+
var res Source
20+
empty := true
21+
first := true
22+
for s := range source {
23+
empty = false
24+
if first {
25+
first = false
26+
res = s
27+
continue
28+
}
29+
res = accumulator(res, s)
2530
}
26-
r := enr.Current()
27-
for enr.MoveNext() {
28-
r = accumulator(r, enr.Current())
31+
if empty {
32+
return generichelper.ZeroValue[Source](), ErrEmptySource
2933
}
30-
return r, nil
31-
}
32-
33-
// AggregateMust is like [Aggregate] but panics in case of error.
34-
func AggregateMust[Source any](source Enumerable[Source], accumulator func(Source, Source) Source) Source {
35-
return errorhelper.Must(Aggregate(source, accumulator))
34+
return res, nil
3635
}
3736

3837
// [AggregateSeed] applies an accumulator function over a sequence.
3938
// The specified seed value is used as the initial accumulator value.
4039
//
4140
// [AggregateSeed]: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
42-
func AggregateSeed[Source, Accumulate any](source Enumerable[Source],
41+
func AggregateSeed[Source, Accumulate any](source iter.Seq[Source],
4342
seed Accumulate, accumulator func(Accumulate, Source) Accumulate) (Accumulate, error) {
4443
if source == nil {
4544
return generichelper.ZeroValue[Accumulate](), ErrNilSource
4645
}
4746
if accumulator == nil {
4847
return generichelper.ZeroValue[Accumulate](), ErrNilAccumulator
4948
}
50-
enr := source.GetEnumerator()
51-
r := seed
52-
for enr.MoveNext() {
53-
r = accumulator(r, enr.Current())
49+
res := seed
50+
for s := range source {
51+
res = accumulator(res, s)
5452
}
55-
return r, nil
56-
}
57-
58-
// AggregateSeedMust is like [AggregateSeed] but panics in case of error.
59-
func AggregateSeedMust[Source, Accumulate any](source Enumerable[Source],
60-
seed Accumulate, accumulator func(Accumulate, Source) Accumulate) Accumulate {
61-
return errorhelper.Must(AggregateSeed(source, seed, accumulator))
53+
return res, nil
6254
}
6355

6456
// [AggregateSeedSel] applies an accumulator function over a sequence.
6557
// The specified seed value is used as the initial accumulator value,
6658
// and the specified function is used to select the result value.
6759
//
6860
// [AggregateSeedSel]: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
69-
func AggregateSeedSel[Source, Accumulate, Result any](source Enumerable[Source], seed Accumulate,
61+
func AggregateSeedSel[Source, Accumulate, Result any](source iter.Seq[Source], seed Accumulate,
7062
accumulator func(Accumulate, Source) Accumulate, resultSelector func(Accumulate) Result) (Result, error) {
7163
if source == nil {
7264
return generichelper.ZeroValue[Result](), ErrNilSource
@@ -77,16 +69,9 @@ func AggregateSeedSel[Source, Accumulate, Result any](source Enumerable[Source],
7769
if resultSelector == nil {
7870
return generichelper.ZeroValue[Result](), ErrNilSelector
7971
}
80-
enr := source.GetEnumerator()
81-
r := seed
82-
for enr.MoveNext() {
83-
r = accumulator(r, enr.Current())
72+
res := seed
73+
for s := range source {
74+
res = accumulator(res, s)
8475
}
85-
return resultSelector(r), nil
86-
}
87-
88-
// AggregateSeedSelMust is like [AggregateSeedSel] but panics in case of error.
89-
func AggregateSeedSelMust[Source, Accumulate, Result any](source Enumerable[Source], seed Accumulate,
90-
accumulator func(Accumulate, Source) Accumulate, resultSelector func(Accumulate) Result) Result {
91-
return errorhelper.Must(AggregateSeedSel(source, seed, accumulator, resultSelector))
76+
return resultSelector(res), nil
9277
}

aggregate_test.go

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package go2linq
22

33
import (
44
"fmt"
5+
"iter"
56
"reflect"
67
"strings"
78
"testing"
@@ -11,7 +12,7 @@ import (
1112

1213
func TestAggregate_int(t *testing.T) {
1314
type args struct {
14-
source Enumerable[int]
15+
source iter.Seq[int]
1516
accumulator func(int, int) int
1617
}
1718
tests := []struct {
@@ -31,15 +32,15 @@ func TestAggregate_int(t *testing.T) {
3132
},
3233
{name: "NullFuncUnseeded",
3334
args: args{
34-
source: NewEnSlice(1, 3),
35+
source: VarAll(1, 3),
3536
accumulator: nil,
3637
},
3738
wantErr: true,
3839
expectedErr: ErrNilAccumulator,
3940
},
4041
{name: "UnseededAggregation",
4142
args: args{
42-
source: NewEnSlice(1, 4, 5),
43+
source: VarAll(1, 4, 5),
4344
accumulator: func(ag, el int) int { return ag*2 + el },
4445
},
4546
want: 17,
@@ -54,14 +55,14 @@ func TestAggregate_int(t *testing.T) {
5455
},
5556
{name: "UnseededSingleElementAggregation",
5657
args: args{
57-
source: NewEnSlice(1),
58+
source: VarAll(1),
5859
accumulator: func(ag, el int) int { return ag*2 + el },
5960
},
6061
want: 1,
6162
},
6263
{name: "FirstElementOfInputIsUsedAsSeedForUnseededOverload",
6364
args: args{
64-
source: NewEnSlice(5, 3, 2),
65+
source: VarAll(5, 3, 2),
6566
accumulator: func(ag, el int) int { return ag * el },
6667
},
6768
want: 30,
@@ -89,7 +90,7 @@ func TestAggregate_int(t *testing.T) {
8990

9091
func TestAggregateSeed_int_int(t *testing.T) {
9192
type args struct {
92-
source Enumerable[int]
93+
source iter.Seq[int]
9394
seed int
9495
accumulator func(int, int) int
9596
}
@@ -111,7 +112,7 @@ func TestAggregateSeed_int_int(t *testing.T) {
111112
},
112113
{name: "NullFuncSeeded",
113114
args: args{
114-
source: NewEnSlice(1, 3),
115+
source: VarAll(1, 3),
115116
seed: 5,
116117
accumulator: nil,
117118
},
@@ -120,7 +121,7 @@ func TestAggregateSeed_int_int(t *testing.T) {
120121
},
121122
{name: "SeededAggregation",
122123
args: args{
123-
source: NewEnSlice(1, 4, 5),
124+
source: VarAll(1, 4, 5),
124125
seed: 5,
125126
accumulator: func(ac, el int) int { return ac*2 + el },
126127
},
@@ -155,20 +156,21 @@ func TestAggregateSeed_int_int(t *testing.T) {
155156
}
156157
}
157158

158-
func TestAggregateSeedMust_int32_int64(t *testing.T) {
159+
func TestAggregateSeed_int32_int64(t *testing.T) {
159160
type args struct {
160-
source Enumerable[int32]
161+
source iter.Seq[int32]
161162
seed int64
162163
accumulator func(int64, int32) int64
163164
}
164165
tests := []struct {
165-
name string
166-
args args
167-
want int64
166+
name string
167+
args args
168+
want int64
169+
wantErr bool
168170
}{
169171
{name: "DifferentSourceAndAccumulatorTypes",
170172
args: args{
171-
source: NewEnSlice(int32(2000000000), int32(2000000000), int32(2000000000)),
173+
source: VarAll(int32(2000000000), int32(2000000000), int32(2000000000)),
172174
seed: int64(0),
173175
accumulator: func(ac int64, el int32) int64 { return ac + int64(el) },
174176
},
@@ -177,17 +179,21 @@ func TestAggregateSeedMust_int32_int64(t *testing.T) {
177179
}
178180
for _, tt := range tests {
179181
t.Run(tt.name, func(t *testing.T) {
180-
got := AggregateSeedMust(tt.args.source, tt.args.seed, tt.args.accumulator)
182+
got, err := AggregateSeed(tt.args.source, tt.args.seed, tt.args.accumulator)
183+
if (err != nil) != tt.wantErr {
184+
t.Errorf("AggregateSeed() error = %v, wantErr %v", err, tt.wantErr)
185+
return
186+
}
181187
if !reflect.DeepEqual(got, tt.want) {
182-
t.Errorf("AggregateSeedMust() = %v, want %v", got, tt.want)
188+
t.Errorf("AggregateSeed() = %v, want %v", got, tt.want)
183189
}
184190
})
185191
}
186192
}
187193

188194
func TestAggregateSeedSel_int_int_string(t *testing.T) {
189195
type args struct {
190-
source Enumerable[int]
196+
source iter.Seq[int]
191197
seed int
192198
accumulator func(int, int) int
193199
resultSelector func(int) string
@@ -211,7 +217,7 @@ func TestAggregateSeedSel_int_int_string(t *testing.T) {
211217
},
212218
{name: "NullFuncSeededWithResultSelector",
213219
args: args{
214-
source: NewEnSlice(1, 3),
220+
source: VarAll(1, 3),
215221
seed: 5,
216222
accumulator: nil,
217223
resultSelector: func(r int) string { return fmt.Sprint(r) },
@@ -221,7 +227,7 @@ func TestAggregateSeedSel_int_int_string(t *testing.T) {
221227
},
222228
{name: "NullProjectionSeededWithResultSelector",
223229
args: args{
224-
source: NewEnSlice(1, 3),
230+
source: VarAll(1, 3),
225231
seed: 5,
226232
accumulator: func(ac, el int) int { return ac + el },
227233
resultSelector: nil,
@@ -231,7 +237,7 @@ func TestAggregateSeedSel_int_int_string(t *testing.T) {
231237
},
232238
{name: "SeededAggregationWithResultSelector",
233239
args: args{
234-
source: NewEnSlice(1, 4, 5),
240+
source: VarAll(1, 4, 5),
235241
seed: 5,
236242
accumulator: func(ac, el int) int { return ac*2 + el },
237243
resultSelector: func(r int) string { return fmt.Sprint(r) },
@@ -268,29 +274,29 @@ func TestAggregateSeedSel_int_int_string(t *testing.T) {
268274
}
269275
}
270276

271-
// see the last example from Enumerable.Aggregate help
277+
// last example from
272278
// https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
273-
func ExampleAggregateMust() {
279+
func ExampleAggregate() {
274280
sentence := "the quick brown fox jumps over the lazy dog"
275281
// Split the string into individual words.
276282
words := strings.Fields(sentence)
277283
// Prepend each word to the beginning of the new sentence to reverse the word order.
278-
reversed := AggregateMust(
279-
NewEnSlice(words...),
284+
reversed, _ := Aggregate(
285+
SliceAll(words),
280286
func(workingSentence, next string) string { return next + " " + workingSentence },
281287
)
282288
fmt.Println(reversed)
283289
// Output:
284290
// dog lazy the over jumps fox brown quick the
285291
}
286292

287-
// see the second example from Enumerable.Aggregate help
293+
// second example from
288294
// https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
289-
func ExampleAggregateSeedMust() {
295+
func ExampleAggregateSeed() {
290296
ints := []int{4, 8, 8, 3, 9, 0, 7, 8, 2}
291297
// Count the even numbers in the array, using a seed value of 0.
292-
numEven := AggregateSeedMust(
293-
NewEnSlice(ints...),
298+
numEven, _ := AggregateSeed(
299+
SliceAll(ints),
294300
0,
295301
func(total, next int) int {
296302
if next%2 == 0 {
@@ -304,13 +310,13 @@ func ExampleAggregateSeedMust() {
304310
// The number of even integers is: 6
305311
}
306312

307-
// see the first example from Enumerable.Aggregate help
313+
// first example from
308314
// https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregate
309-
func ExampleAggregateSeedSelMust() {
315+
func ExampleAggregateSeedSel() {
310316
fruits := []string{"apple", "mango", "orange", "passionfruit", "grape"}
311317
// Determine whether any string in the array is longer than "banana".
312-
longestName := AggregateSeedSelMust(
313-
NewEnSlice(fruits...),
318+
longestName, _ := AggregateSeedSel(
319+
SliceAll(fruits),
314320
"banana",
315321
func(longest, next string) string {
316322
if len(next) > len(longest) {

0 commit comments

Comments
 (0)