Skip to content

Commit 6d56281

Browse files
authored
feat: Accum, ReduceOK (#29)
feat(slice,loop): Accum, ReduceOK methods.
1 parent 0d1111f commit 6d56281

File tree

31 files changed

+726
-120
lines changed

31 files changed

+726
-120
lines changed

README.md

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,18 @@ var sum = slice.Reduce([]int{1, 2, 3, 4, 5, 6}, func(i1, i2 int) int { return i1
258258
//21
259259
```
260260

261+
##### slice.Accum
262+
263+
``` go
264+
import (
265+
"github.com/m4gshm/gollections/op"
266+
"github.com/m4gshm/gollections/slice"
267+
)
268+
269+
var sum = slice.Accum(100, slice.Of(1, 2, 3, 4, 5, 6), op.Sum)
270+
//121
271+
```
272+
261273
##### slice.First
262274

263275
``` go
@@ -455,9 +467,10 @@ type (
455467
)
456468
```
457469

458-
The `Loop` function retrieves a next element from a dataset and returns
459-
`ok==true` if successful.
460-
The `KVLoop` behaves similar but returns a key/value pair.
470+
The `Loop` function returns a next element from a dataset and returns
471+
`ok==true` on success. `ok==false` means there are no more elements in
472+
the dataset.
473+
The `KVLoop` behaves similar but returns key/value pairs.
461474

462475
``` go
463476
even := func(i int) bool { return i%2 == 0 }
@@ -553,7 +566,10 @@ ageGroupedSortedNames = loop.ToMapResolv(loop.Of(users...), func(u User) string
553566
##### sum.Of
554567

555568
``` go
556-
import "github.com/m4gshm/gollections/op/sum"
569+
import (
570+
"github.com/m4gshm/gollections/loop"
571+
"github.com/m4gshm/gollections/loop/sum"
572+
)
557573

558574
var sum = sum.Of(loop.Of(1, 2, 3, 4, 5, 6)) //21
559575
```
@@ -565,6 +581,31 @@ var sum = loop.Reduce(loop.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i
565581
//21
566582
```
567583

584+
##### loop.ReduceOK
585+
586+
``` go
587+
adder := func(i1, i2 int) int { return i1 + i2 }
588+
589+
sum, ok := loop.ReduceOK(loop.Of(1, 2, 3, 4, 5, 6), adder)
590+
//21, true
591+
592+
emptyLoop := loop.Of[int]()
593+
sum, ok = loop.ReduceOK(emptyLoop, adder)
594+
//0, false
595+
```
596+
597+
##### loop.Accum
598+
599+
``` go
600+
import (
601+
"github.com/m4gshm/gollections/loop"
602+
"github.com/m4gshm/gollections/op"
603+
)
604+
605+
var sum = loop.Accum(100, loop.Of(1, 2, 3, 4, 5, 6), op.Sum)
606+
//121
607+
```
608+
568609
##### loop.First
569610

570611
``` go

break/kv/loop/api.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ func New[S, K, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, err
2323

2424
// From wrap the next loop to a breakable loop
2525
func From[K, V any](next func() (K, V, bool)) func() (K, V, bool, error) {
26+
if next == nil {
27+
return nil
28+
}
2629
return func() (K, V, bool, error) {
2730
k, v, ok := next()
2831
return k, v, ok, nil
@@ -32,6 +35,9 @@ func From[K, V any](next func() (K, V, bool)) func() (K, V, bool, error) {
3235
// To transforms a breakable loop to a simple loop.
3336
// The errConsumer is a function that is called when an error occurs.
3437
func To[K, V any](next func() (K, V, bool, error), errConsumer func(error)) func() (K, V, bool) {
38+
if next == nil {
39+
return nil
40+
}
3541
return func() (K, V, bool) {
3642
k, v, ok, err := next()
3743
if err != nil {
@@ -47,40 +53,56 @@ func Group[K comparable, V any](next func() (K, V, bool, error)) (map[K][]V, err
4753
return ToMapResolv(next, resolv.Slice[K, V])
4854
}
4955

50-
// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function
51-
func Reduce[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V)) (rk K, rv V, err error) {
56+
// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
57+
// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned.
58+
func Reduce[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V)) (K, V, error) {
59+
rk, rv, _, err := ReduceOK(next, merge)
60+
return rk, rv, err
61+
}
62+
63+
// ReduceOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
64+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
65+
func ReduceOK[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V)) (rk K, rv V, ok bool, err error) {
5266
if next == nil {
53-
return rk, rv, nil
67+
return rk, rv, false, nil
5468
}
5569
k, v, ok, err := next()
5670
if err != nil || !ok {
57-
return rk, rv, err
71+
return k, v, ok, err
5872
}
5973
rk, rv = k, v
6074
for {
6175
k, v, ok, err := next()
6276
if err != nil || !ok {
63-
return rk, rv, err
77+
return rk, rv, true, err
6478
}
6579
rk, rv = merge(rk, k, rv, v)
6680
}
6781
}
6882

69-
// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function
70-
func Reducee[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V, error)) (rk K, rv V, err error) {
83+
// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
84+
// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned.
85+
func Reducee[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V, error)) (K, V, error) {
86+
rk, rv, _, err := ReduceeOK(next, merge)
87+
return rk, rv, err
88+
}
89+
90+
// ReduceeOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
91+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
92+
func ReduceeOK[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V, error)) (rk K, rv V, ok bool, err error) {
7193
if next == nil {
72-
return rk, rv, nil
94+
return rk, rv, false, nil
7395
}
7496
k, v, ok, err := next()
7597
if err != nil || !ok {
76-
return rk, rv, err
98+
return rk, rv, ok, err
7799
}
78100
rk, rv = k, v
79101
for {
80102
if k, v, ok, err := next(); err != nil || !ok {
81-
return rk, rv, err
103+
return rk, rv, true, err
82104
} else if rk, rv, err = merge(rk, k, rv, v); err != nil {
83-
return rk, rv, err
105+
return rk, rv, true, err
84106
}
85107
}
86108
}
@@ -145,7 +167,7 @@ func Conv[K, V any, KOUT, VOUT any](next func() (K, V, bool, error), converter f
145167
return k2, v2, false, err
146168
}
147169
k2, v2, err = converter(k, v)
148-
return k2, v2, err == nil, err
170+
return k2, v2, true, err
149171
}
150172
}
151173

break/kv/loop/loop.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@ func (next Loop[K, V]) First(predicate func(K, V) bool) (K, V, bool, error) {
1313
return First(next, predicate)
1414
}
1515

16-
// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
16+
// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
17+
// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned.
1718
func (next Loop[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (K, V, error) {
1819
return Reduce(next, merge)
1920
}
2021

21-
// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
22+
// ReduceOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
23+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
24+
func (next Loop[K, V]) ReduceOK(merge func(K, K, V, V) (K, V)) (K, V, bool, error) {
25+
return ReduceOK(next, merge)
26+
}
27+
28+
// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
29+
// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned.
2230
func (next Loop[K, V]) Reducee(merge func(K, K, V, V) (K, V, error)) (K, V, error) {
2331
return Reducee(next, merge)
2432
}
2533

34+
// ReduceeOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function.
35+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
36+
func (next Loop[K, V]) ReduceeOK(merge func(K, K, V, V) (K, V, error)) (K, V, bool, error) {
37+
return ReduceeOK(next, merge)
38+
}
39+
2640
// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful
2741
func (next Loop[K, V]) HasAny(predicate func(K, V) bool) (bool, error) {
2842
return HasAny(next, predicate)

break/kv/loop/test/api_test.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,57 @@ func Test_Firstt(t *testing.T) {
4444
func Test_Reduce(t *testing.T) {
4545
kvl := breakkvloop.From(loop.KeyValue(loop.Of(k.V(1, "one"), k.V(2, "two"), k.V(3, "three")), c.KV[int, string].Key, c.KV[int, string].Value))
4646

47-
k, v, _ := breakkvloop.Reduce(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr })
47+
k, v, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr })
4848

49+
assert.True(t, ok)
4950
assert.Equal(t, 1+2+3, k)
5051
assert.Equal(t, "one"+"two"+"three", v)
5152
}
5253

54+
func Test_Reduce_Empty(t *testing.T) {
55+
kvl := breakkvloop.From(loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value))
56+
57+
_, _, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr })
58+
59+
assert.False(t, ok)
60+
}
61+
62+
func Test_Reduce_Nil(t *testing.T) {
63+
var l loop.Loop[c.KV[int, string]]
64+
kvl := breakkvloop.From(loop.KeyValue(l, c.KV[int, string].Key, c.KV[int, string].Value))
65+
66+
_, _, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr })
67+
68+
assert.False(t, ok)
69+
}
70+
5371
func Test_Reducee(t *testing.T) {
5472
kvl := breakkvloop.From(loop.KeyValue(loop.Of(k.V(1, "one"), k.V(2, "two"), k.V(3, "three")), c.KV[int, string].Key, c.KV[int, string].Value))
5573

56-
k, v, _ := breakkvloop.Reducee(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil })
74+
k, v, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil })
5775

76+
assert.True(t, ok)
5877
assert.Equal(t, 1+2+3, k)
5978
assert.Equal(t, "one"+"two"+"three", v)
6079
}
6180

81+
func Test_Reducee_Empty(t *testing.T) {
82+
kvl := breakkvloop.From(loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value))
83+
84+
_, _, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil })
85+
86+
assert.False(t, ok)
87+
}
88+
89+
func Test_Reducee_Nil(t *testing.T) {
90+
var l loop.Loop[c.KV[int, string]]
91+
kvl := breakkvloop.From(loop.KeyValue(l, c.KV[int, string].Key, c.KV[int, string].Value))
92+
93+
_, _, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil })
94+
95+
assert.False(t, ok)
96+
}
97+
6298
func Test_Convert(t *testing.T) {
6399
kvl := breakkvloop.From(loop.KeyValue(loop.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3")), c.KV[int, string].Key, c.KV[int, string].Value))
64100

break/loop/api.go

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -200,40 +200,76 @@ func Append[T any, TS ~[]T](next func() (T, bool, error), out TS) (TS, error) {
200200
return out, nil
201201
}
202202

203-
// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
204-
func Reduce[T any](next func() (T, bool, error), merger func(T, T) T) (out T, e error) {
203+
// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
204+
// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned.
205+
func Reduce[T any](next func() (T, bool, error), merge func(T, T) T) (T, error) {
206+
result, _, err := ReduceOK(next, merge)
207+
return result, err
208+
}
209+
210+
// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
211+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
212+
func ReduceOK[T any](next func() (T, bool, error), merge func(T, T) T) (result T, ok bool, err error) {
205213
if next == nil {
206-
return out, nil
214+
return result, false, nil
215+
}
216+
if result, ok, err = next(); err != nil || !ok {
217+
return result, ok, err
218+
}
219+
result, err = Accum(result, next, merge)
220+
return result, true, err
221+
}
222+
223+
// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
224+
// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned.
225+
func Reducee[T any](next func() (T, bool, error), merge func(T, T) (T, error)) (T, error) {
226+
result, _, err := ReduceeOK(next, merge)
227+
return result, err
228+
}
229+
230+
// ReduceeOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function.
231+
// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements).
232+
func ReduceeOK[T any](next func() (T, bool, error), merge func(T, T) (T, error)) (result T, ok bool, err error) {
233+
if next == nil {
234+
return result, false, nil
235+
}
236+
if result, ok, err = next(); err != nil || !ok {
237+
return result, ok, err
207238
}
208-
v, ok, err := next()
209-
if err != nil || !ok {
210-
return out, err
239+
result, err = Accumm(result, next, merge)
240+
return result, true, err
241+
}
242+
243+
// Accum accumulates a value by using the 'first' argument to initialize the accumulator and sequentially applying the 'merge' functon to the accumulator and each element retrieved by the 'next' function.
244+
func Accum[T any](first T, next func() (T, bool, error), merge func(T, T) T) (accumulator T, err error) {
245+
accumulator = first
246+
if next == nil {
247+
return accumulator, nil
211248
}
212-
out = v
213249
for {
214250
v, ok, err := next()
215-
if err != nil || !ok {
216-
return out, err
251+
if err != nil {
252+
return accumulator, err
253+
} else if !ok {
254+
return accumulator, nil
217255
}
218-
out = merger(out, v)
256+
accumulator = merge(accumulator, v)
219257
}
220258
}
221259

222-
// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function
223-
func Reducee[T any](next func() (T, bool, error), merger func(T, T) (T, error)) (out T, e error) {
260+
// Accumm accumulates a value by using the 'first' argument to initialize the accumulator and sequentially applying the 'merge' functon to the accumulator and each element retrieved by the 'next' function.
261+
func Accumm[T any](first T, next func() (T, bool, error), merge func(T, T) (T, error)) (accumulator T, err error) {
262+
accumulator = first
224263
if next == nil {
225-
return out, nil
226-
}
227-
v, ok, err := next()
228-
if err != nil || !ok {
229-
return out, err
264+
return accumulator, nil
230265
}
231-
out = v
232266
for {
233267
if v, ok, err := next(); err != nil || !ok {
234-
return out, err
235-
} else if out, err = merger(out, v); err != nil {
236-
return out, err
268+
return accumulator, err
269+
} else if v, err = merge(accumulator, v); err != nil {
270+
return accumulator, err
271+
} else {
272+
accumulator = v
237273
}
238274
}
239275
}
@@ -608,7 +644,7 @@ func KeyValuee[T any, K, V any](next func() (T, bool, error), keyExtractor func(
608644
return key, value, false, err
609645
} else if key, err = keyExtractor(elem); err == nil {
610646
value, err = valExtractor(elem)
611-
return key, value, err == nil, err
647+
return key, value, true, err
612648
}
613649
return key, value, false, nil
614650
}
@@ -850,13 +886,13 @@ func ToMapResolvv[T any, K comparable, V, VR any](
850886
}
851887

852888
// ConvertAndReduce converts each elements and merges them into one
853-
func ConvertAndReduce[From, To any](next func() (From, bool, error), converter func(From) To, merger func(To, To) To) (out To, err error) {
854-
return Reduce(Convert(next, converter), merger)
889+
func ConvertAndReduce[From, To any](next func() (From, bool, error), converter func(From) To, merge func(To, To) To) (out To, err error) {
890+
return Reduce(Convert(next, converter), merge)
855891
}
856892

857893
// ConvAndReduce converts each elements and merges them into one
858-
func ConvAndReduce[From, To any](next func() (From, bool, error), converter func(From) (To, error), merger func(To, To) To) (out To, err error) {
859-
return Reduce(Conv(next, converter), merger)
894+
func ConvAndReduce[From, To any](next func() (From, bool, error), converter func(From) (To, error), merge func(To, To) To) (out To, err error) {
895+
return Reduce(Conv(next, converter), merge)
860896
}
861897

862898
// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag.

0 commit comments

Comments
 (0)