diff --git a/Makefile b/Makefile index 080d93fa..811c0c1f 100644 --- a/Makefile +++ b/Makefile @@ -67,9 +67,9 @@ lint: # go install golang.org/x/lint/golint@latest # golint ./... go install github.com/mgechev/revive@latest - revive -exclude internal/... ./... - go install github.com/alexkohler/prealloc@latest - prealloc ./... + revive -exclude internal/examples/... -exclude internal/benchmark/... ./... +# go install github.com/alexkohler/prealloc@latest +# prealloc ./... .PHONY: readme readme: diff --git a/README.md b/README.md index dbf7541e..9611ccd7 100644 --- a/README.md +++ b/README.md @@ -514,12 +514,6 @@ ints, err := seqe.Slice(intSeq) //[1 2 3], invalid syntax ### Sequence API -To use any collection or loop as a rangefunc sequecne just call -[All](#iterating-over-collections) method of that one. - -In many cases the API likes the -[loop](#loop-kvloop-and-breakable-versions-breakloop-breakkvloop) API. - #### Instantiators ##### seq.Of, seq2.Of, seq2.OfMap @@ -529,15 +523,16 @@ import( "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/seq2" ) - var ( - ints iter.Seq[int] = seq.Of(1, 2, 3) - pairs iter.Seq2[string, int] = seq2.OfMap(map[string]int{ + ints seq.Seq[int] = seq.Of(1, 2, 3) + pairs seq.Seq2[string, int] = seq2.OfMap(map[string]int{ "first": 1, "second": 2, "third": 3, }) ) + +assert.Equal(t, []int{3, 2, 1}, sort.Desc(pairs.Values().Slice())) ``` ##### seq.OfNext, seqe.OfNext, seq.OfNextGet, seqe.OfNextGet @@ -552,7 +547,8 @@ import( var rows sql.Rows = selectUsers() -rowSeq := seqe.OfNext(rows.Next, func(u *User) error { return rows.Scan(&u.name, &u.age) }) +getUser := func(u *User) error { return rows.Scan(&u.name, &u.age) } +rowSeq := seqe.OfNext(rows.Next, getUser) usersByAge, err := seqe.Group(rowSeq, User.Age, as.Is) ``` @@ -599,7 +595,10 @@ import( ) var numbers, factorials []int -for i, n := range seq2.Series(1, func(i int, prev int) (int, bool) { return i * prev, i <= 5 }) { +next := func(i, prev int) (int, bool) { + return i * prev, i <= 5 +} +for i, n := range seq2.Series(1, next) { numbers = append(numbers, i) factorials = append(factorials, n) } @@ -613,7 +612,14 @@ for i, n := range seq2.Series(1, func(i int, prev int) (int, bool) { return i * ``` go filter := func(u User) bool { return u.age <= 30 } -names := seq.Slice(seq.Convert(seq.Filter(seq.Of(users...), filter), User.Name)) +less30Names := seq.Convert(seq.Of(users...).Filter(filter), User.Name) + +names := seq.Slice(less30Names) +//[Bob Tom] + + +//or +names = less30Names.Slice() //[Bob Tom] ``` @@ -629,14 +635,15 @@ import ( "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/sort" ) - -var users iter.Seq[User] = seq.Of(users...) -var groups iter.Seq2[string, User] = seq.ToSeq2(users, func(u User) (string, User) { +var users seq.Seq[User] = seq.Of(users...) +var groups seq.Seq2[string, User] = seq.ToSeq2(users, func(u User) (string, User) { return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30"), u }) var ageGroups map[string][]User = seq2.Group(groups) //map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]] + +assert.Equal(t, slice.Of("Alice", "Chris"), sort.Asc(slice.Convert(ageGroups[">30"], User.Name))) ``` #### Reducers @@ -644,7 +651,13 @@ var ageGroups map[string][]User = seq2.Group(groups) ##### seq.Reduce ``` go -var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 + i2 }) +adder := func(i1, i2 int) int { return i1 + i2 } +var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), adder) +//21 + + +//or +sum = seq.Of(1, 2, 3, 4, 5, 6).Reduce(adder) //21 ``` @@ -652,12 +665,13 @@ var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 ``` go adder := func(i1, i2 int) int { return i1 + i2 } - sum, ok := seq.ReduceOK(seq.Of(1, 2, 3, 4, 5, 6), adder) //21, true + +//or emptyLoop := seq.Of[int]() -sum, ok = seq.ReduceOK(emptyLoop, adder) +sum, ok = emptyLoop.ReduceOK(adder) //0, false ``` @@ -670,6 +684,10 @@ import ( ) result, ok := seq.First(seq.Of(1, 3, 5, 7, 9, 11), more.Than(5)) //7, true + + +//or +result, ok = seq.Of(1, 3, 5, 7, 9, 11).First(more.Than(5)) //7, true ``` ##### seq.Head @@ -680,6 +698,10 @@ import ( ) result, ok := seq.Head(seq.Of(1, 3, 5, 7, 9, 11)) //1, true + + +//or +result, ok = seq.Of(1, 3, 5, 7, 9, 11).Head() //1, true ``` #### Element converters @@ -739,7 +761,7 @@ import ( var f1 = seq.Slice(seq.Filter(seq.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11)))) //[]int{1, 7, 11} -var f2 = seq.Slice(seq.Filter(seq.Of(1, 3, 5, 7, 9, 11), exclude.All(1, 7, 11))) +var f2 = seq.Of(1, 3, 5, 7, 9, 11).Filter(exclude.All(1, 7, 11)).Slice() //[]int{3, 5, 9} ``` @@ -750,308 +772,44 @@ import ( "github.com/m4gshm/gollections/seq" ) -var i []int = seq.Slice(seq.Top(4, seq.Of(1, 3, 5, 7, 9, 11))) +i := seq.Slice(seq.Top(4, seq.Of(1, 3, 5, 7, 9, 11))) //[]int{1, 3, 5, 7} -``` - -##### seq.Skip -``` go -import ( - "github.com/m4gshm/gollections/seq" -) -var i []int = seq.Slice(seq.Skip(4, seq.Of(1, 3, 5, 7, 9, 11))) -//[]int{9, 11} +//or +i = seq.Of(1, 3, 5, 7, 9, 11).Top(4).Slice() +//[]int{1, 3, 5, 7} ``` -##### seq.Flat, seq.FlatSeq, seqe.Flat, seqe.FlatSeq +##### seq.Skip ``` go import ( - "github.com/m4gshm/gollections/convert/as" "github.com/m4gshm/gollections/seq" ) -var i []int = seq.Slice(seq.Flat(seq.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), as.Is)) -//[]int{1, 2, 3, 4, 5, 6} -``` - -## [loop](./loop/api.go), [kv/loop](./kv/loop/api.go) and breakable versions [break/loop](./break/loop/api.go), [break/kv/loop](./break/kv/loop/api.go) - -**Deprecated**: will be replaced by [seq](#seq-seq2-seqe) API. - -Legacy iterators API based on the following functions: - -``` go -type ( - Loop[T any] func() (element T, ok bool) - KVLoop[K, V any] func() (key K, value V, ok bool) - BreakLoop[T any] func() (element T, ok bool, err error) - BreakKVLoop[K, V any] func() (key K, value V, ok bool, err error) -) -``` - -The `Loop` function returns a next element from a dataset and returns -`ok==true` on success. `ok==false` means there are no more elements in -the dataset. -The `KVLoop` behaves similar but returns key/value pairs. - -``` go -even := func(i int) bool { return i%2 == 0 } -seq := loop.Convert(loop.Filter(loop.Of(1, 2, 3, 4), even), strconv.Itoa) -var result []string = seq.Slice() //[2 4] -``` - -`BreakLoop` and `BreakKVLoop` are used for sources that can issue an -error. - -``` go -intSeq := loop.Conv(loop.Of("1", "2", "3", "ddd4", "5"), strconv.Atoi) -ints, err := loop.Slice(intSeq) //[1 2 3], invalid syntax -``` - -The API in most cases is similar to the [slice](./slice/api.go) API but -with delayed computation which means that the methods don’t compute a -result but only return a loop provider. The loop provider is type with a -`Next` method that returns a next processed element. - -### Main loop functions - -#### Instantiators - -##### loop.Of, loop.S - -``` go -import "github.com/m4gshm/gollections/loop" - -var ( - ints = loop.Of(1, 2, 3) - strings = loop.S([]string{"a", "b", "c"}) -) -``` - -##### range\_.Of - -``` go -import "github.com/m4gshm/gollections/loop/range_" - -var increasing = range_.Of(-1, 3).Slice() //[]int{-1, 0, 1, 2} -var decreasing = range_.Of('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b'} -var nothing = range_.Of(1, 1).Slice() //nil -``` - -##### range\_.Closed - -``` go -var increasing = range_.Closed(-1, 3).Slice() //[]int{-1, 0, 1, 2, 3} -var decreasing = range_.Closed('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b', 'a'} -var one = range_.Closed(1, 1).Slice() //[]int{1} -``` - -#### Collectors - -##### loop.Slice - -``` go -filter := func(u User) bool { return u.age <= 30 } -names := loop.Slice(loop.Convert(loop.Filter(loop.Of(users...), filter), User.Name)) -//[Bob Tom] -``` - -##### group.Of - -``` go -import ( - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/expr/use" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/group" -) - -var ageGroups map[string][]User = group.Of(loop.Of(users...), func(u User) string { - return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30") -}, as.Is) - -//map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]] -``` - -##### loop.Map, loop.MapResolv - -``` go -import ( - "github.com/m4gshm/gollections/map_/resolv" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/loop" -) - -var ageGroupedSortedNames map[string][]string - -ageGroupedSortedNames = loop.MapResolv(loop.Of(users...), func(u User) string { - return op.IfElse(u.age <= 30, "<=30", ">30") -}, User.Name, resolv.SortedSlice) - -//map[<=30:[Bob Tom] >30:[Alice Chris]] -``` - -#### Reducers - -##### sum.Of - -``` go -import ( - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/sum" -) - -var sum = sum.Of(loop.Of(1, 2, 3, 4, 5, 6)) //21 -``` - -##### loop.Reduce - -``` go -var sum = loop.Reduce(loop.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 + i2 }) -//21 -``` - -##### loop.ReduceOK - -``` go -adder := func(i1, i2 int) int { return i1 + i2 } - -sum, ok := loop.ReduceOK(loop.Of(1, 2, 3, 4, 5, 6), adder) -//21, true - -emptyLoop := loop.Of[int]() -sum, ok = loop.ReduceOK(emptyLoop, adder) -//0, false -``` - -##### loop.Accum - -``` go -import ( - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" -) - -var sum = loop.Accum(100, loop.Of(1, 2, 3, 4, 5, 6), op.Sum) -//121 -``` - -##### loop.First - -``` go -import ( - "github.com/m4gshm/gollections/predicate/more" - "github.com/m4gshm/gollections/loop" -) - -result, ok := loop.First(loop.Of(1, 3, 5, 7, 9, 11), more.Than(5)) //7, true -``` - -#### Element converters - -##### loop.Convert - -``` go -var s []string = loop.Convert(loop.Of(1, 3, 5, 7, 9, 11), strconv.Itoa).Slice() -//[]string{"1", "3", "5", "7", "9", "11"} -``` - -##### loop.Conv - -``` go -result, err := loop.Conv(loop.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi).Slice() -//[]int{1, 3, 5}, ErrSyntax -``` - -#### Loop converters - -##### loop.Filter - -``` go -import ( - "github.com/m4gshm/gollections/predicate/exclude" - "github.com/m4gshm/gollections/predicate/one" - "github.com/m4gshm/gollections/loop" -) +i := seq.Slice(seq.Skip(4, seq.Of(1, 3, 5, 7, 9, 11))) +//[]int{9, 11} -var f1 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11))).Slice() -//[]int{1, 7, 11} -var f2 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), exclude.All(1, 7, 11)).Slice() -//[]int{3, 5, 9} +//or +i = seq.Of(1, 3, 5, 7, 9, 11).Skip(4).Slice() +//[]int{9, 11} ``` -##### loop.Flat +##### seq.Flat, seq.FlatSeq, seqe.Flat, seqe.FlatSeq ``` go import ( "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/seq" ) -var i []int = loop.Flat(loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), as.Is).Slice() +twoDimensions := [][]int{{1, 2, 3}, {4}, {5, 6}} +var i []int = seq.Slice(seq.Flat(seq.Of(twoDimensions...), as.Is)) //[]int{1, 2, 3, 4, 5, 6} ``` -#### Operations chain functions - -- convert.AndReduce, conv.AndReduce - -- convert.AndFilter - -- filter.AndConvert - -These functions combine converters, filters and reducers. - -### Iterating over loops - -- Using rangefunc `All` like: - -``` go -for i := range range_.Of(0, 100).All { - doOp(i) -} -``` - -- Using `for` statement like: - -``` go -next := range_.Of(0, 100) -for i, ok := next(); ok; i, ok = next() { - doOp(i) -} -``` - -- or - -``` go -for next, i, ok := range_.Of(0, 100).Crank(); ok; i, ok = next() { - doOp(i) -} -``` - -- `ForEach` method - -``` go -range_.Of(0, 100).ForEach(doOp) -``` - -- or `For` method that can be aborted by returning `Break` for expected - completion, or another error otherwise. - -``` go -range_.Of(0, 100).For(func(i int) error { - if i > 22 { - return loop.Break - } - doOp(i) - return loop.Continue -}) -``` - ## Data structures ### [mutable](./collection/mutable/api.go) and [immutable](./collection/immutable/api.go) collections @@ -1191,29 +949,17 @@ The same underlying interfaces but for read-only use cases. - Using rangefunc `All` like: ``` go -uniques := set.From(range_.Of(0, 100)) -for i := range uniques.All { - doOp(i) + uniques := set.Of(1, 2, 3, 4, 5, 6) + for i := range uniques.All { + doOp(i) + } + } ``` - `ForEach` method ``` go -uniques := set.From(range_.Of(0, 100)) +uniques := set.Of(1, 2, 3, 4, 5, 6) uniques.ForEach(doOp) ``` - -- or `For` method that can be aborted by returning `Break` for expected - completion, or another error otherwise. - -``` go -uniques := set.From(range_.Of(0, 100)) -uniques.For(func(i int) error { - if i > 22 { - return loop.Break - } - doOp(i) - return loop.Continue -}) -``` diff --git a/break/map_/convert/api.go b/break/kv/convert/api.go similarity index 100% rename from break/map_/convert/api.go rename to break/kv/convert/api.go diff --git a/break/kv/iface.go b/break/kv/iface.go deleted file mode 100644 index f8815588..00000000 --- a/break/kv/iface.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package kv provides key/value types, functions -package kv - -import "github.com/m4gshm/gollections/c" - -// Iterator provides iterate over key/value pairs, where an iteration can be interrupted by an error -type Iterator[K, V any] interface { - // Next returns the next key/value pair. - // The ok result indicates whether the element was returned by the iterator. - // If ok == false, then the iteration must be completed. - Next() (key K, value V, ok bool, err error) - c.Track[K, V] -} diff --git a/break/kv/loop/api.go b/break/kv/loop/api.go deleted file mode 100644 index 13b5c801..00000000 --- a/break/kv/loop/api.go +++ /dev/null @@ -1,259 +0,0 @@ -// Package loop provides helpers for loop operation over key/value pairs. -// -// Deprecated: use the [github.com/m4gshm/gollections/seq], [github.com/m4gshm/gollections/seqe], [github.com/m4gshm/gollections/seq2] packages API instead. -package loop - -import ( - "errors" - - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/map_/resolv" -) - -// New is the mai breakable key/value loop constructor -func New[S, K, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, error)) Loop[K, V] { - return func() (k K, v V, ok bool, err error) { - if ok := hasNext(source); !ok { - return k, v, false, nil - } else if k, v, err = getNext(source); err != nil { - return k, v, false, err - } else { - return k, v, true, nil - } - } -} - -// From wrap the next loop to a breakable loop -func From[K, V any](next func() (K, V, bool)) func() (K, V, bool, error) { - if next == nil { - return nil - } - return func() (K, V, bool, error) { - k, v, ok := next() - return k, v, ok, nil - } -} - -// To transforms a breakable loop to a simple loop. -// The errConsumer is a function that is called when an error occurs. -func To[K, V any](next func() (K, V, bool, error), errConsumer func(error)) func() (K, V, bool) { - if next == nil { - return nil - } - return func() (K, V, bool) { - k, v, ok, err := next() - if err != nil { - errConsumer(err) - return k, v, false - } - return k, v, ok - } -} - -// Group collects sets of values grouped by keys obtained by passing a key/value loop. -func Group[K comparable, V any](next func() (K, V, bool, error)) (map[K][]V, error) { - return MapResolv(next, resolv.Slice[K, V]) -} - -// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func Reduce[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V)) (K, V, error) { - rk, rv, _, err := ReduceOK(next, merge) - return rk, rv, err -} - -// ReduceOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -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) { - if next == nil { - return rk, rv, false, nil - } - k, v, ok, err := next() - if err != nil || !ok { - return k, v, ok, err - } - rk, rv = k, v - for { - k, v, ok, err := next() - if err != nil || !ok { - return rk, rv, true, err - } - rk, rv = merge(rk, k, rv, v) - } -} - -// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func Reducee[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V, error)) (K, V, error) { - rk, rv, _, err := ReduceeOK(next, merge) - return rk, rv, err -} - -// ReduceeOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -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) { - if next == nil { - return rk, rv, false, nil - } - k, v, ok, err := next() - if err != nil || !ok { - return rk, rv, ok, err - } - rk, rv = k, v - for { - if k, v, ok, err := next(); err != nil || !ok { - return rk, rv, true, err - } else if rk, rv, err = merge(rk, k, rv, v); err != nil { - return rk, rv, true, err - } - } -} - -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func HasAny[K, V any](next func() (K, V, bool, error), predicate func(K, V) bool) (bool, error) { - _, _, ok, err := First(next, predicate) - return ok, err -} - -// HasAnyy finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func HasAnyy[K, V any](next func() (K, V, bool, error), predicate func(K, V) (bool, error)) (bool, error) { - _, _, ok, err := Firstt(next, predicate) - return ok, err -} - -// First returns the first key/value pair that satisfies the condition of the 'predicate' function -func First[K, V any](next func() (K, V, bool, error), predicate func(K, V) bool) (K, V, bool, error) { - for { - if k, v, ok, err := next(); err != nil || !ok { - return k, v, false, err - } else if ok := predicate(k, v); ok { - return k, v, true, nil - } - } -} - -// Firstt returns the first key/value pair that satisfies the condition of the 'predicate' function -func Firstt[K, V any](next func() (K, V, bool, error), predicate func(K, V) (bool, error)) (K, V, bool, error) { - for { - if k, v, ok, err := next(); err != nil || !ok { - return k, v, false, err - } else if ok, err := predicate(k, v); err != nil || ok { - return k, v, ok, err - } - } -} - -// Convert creates a loop that applies the 'converter' function to iterable key\values. -func Convert[K, V any, KOUT, VOUT any](next func() (K, V, bool, error), converter func(K, V) (KOUT, VOUT)) Loop[KOUT, VOUT] { - if next == nil { - return nil - } - return func() (k2 KOUT, v2 VOUT, ok bool, err error) { - k, v, ok, err := next() - if err != nil || !ok { - return k2, v2, false, err - } - k2, v2 = converter(k, v) - return k2, v2, true, nil - } -} - -// Conv creates a loop that applies the 'converter' function to iterable key\values. -func Conv[K, V any, KOUT, VOUT any](next func() (K, V, bool, error), converter func(K, V) (KOUT, VOUT, error)) Loop[KOUT, VOUT] { - if next == nil { - return nil - } - return func() (k2 KOUT, v2 VOUT, ok bool, err error) { - k, v, ok, err := next() - if err != nil || !ok { - return k2, v2, false, err - } - k2, v2, err = converter(k, v) - return k2, v2, true, err - } -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filter[K, V any](next func() (K, V, bool, error), filter func(K, V) bool) Loop[K, V] { - if next == nil { - return nil - } - return func() (K, V, bool, error) { - return First(next, filter) - } -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filt[K, V any](next func() (K, V, bool, error), filter func(K, V) (bool, error)) Loop[K, V] { - if next == nil { - return nil - } - return func() (K, V, bool, error) { - k, v, ok, err := Firstt(next, filter) - return k, v, ok && err == nil, err - } -} - -// MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values -func MapResolv[K comparable, V, VR any](next func() (K, V, bool, error), resolver func(bool, K, VR, V) VR) (map[K]VR, error) { - m := map[K]VR{} - for { - k, v, ok, err := next() - if err != nil || !ok { - return m, err - } - exists, ok := m[k] - m[k] = resolver(ok, k, exists, v) - } -} - -// Map collects key\value elements into a new map by iterating over the elements -func Map[K comparable, V any](next func() (K, V, bool, error)) (map[K]V, error) { - return MapResolv(next, resolv.First[K, V]) -} - -// Slice collects key\value elements to a slice by iterating over the elements -func Slice[K, V, T any](next func() (K, V, bool, error), converter func(K, V) T) ([]T, error) { - if next == nil { - return nil, nil - } - s := []T{} - for { - key, val, ok, err := next() - if ok { - s = append(s, converter(key, val)) - } - if !ok || err != nil { - return s, err - } - } -} - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. -func Track[K, V any](next func() (K, V, bool, error), consumer func(K, V) error) error { - if next == nil { - return nil - } - for { - if p, v, ok, err := next(); err != nil || !ok { - return err - } else if err := consumer(p, v); err != nil { - return brk(err) - } - } -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func Crank[K, V any](next func() (K, V, bool, error)) (n Loop[K, V], k K, v V, ok bool, err error) { - if next != nil { - k, v, ok, err = next() - } - return next, k, v, ok, err -} - -func brk(err error) error { - if errors.Is(err, c.Break) { - return nil - } - return err -} diff --git a/break/kv/loop/group/api.go b/break/kv/loop/group/api.go deleted file mode 100644 index 2922dc6e..00000000 --- a/break/kv/loop/group/api.go +++ /dev/null @@ -1,11 +0,0 @@ -// Package group provides short aliases for functions thath are used to group key/value pairs retrieved by a loop -package group - -import ( - "github.com/m4gshm/gollections/break/kv/loop" -) - -// Of is a short alias for loop.Group -func Of[K comparable, V any](next func() (K, V, bool, error)) (map[K][]V, error) { - return loop.Group(next) -} diff --git a/break/kv/loop/group/api_test.go b/break/kv/loop/group/api_test.go deleted file mode 100644 index dd8cbd62..00000000 --- a/break/kv/loop/group/api_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package group - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/break/loop" -) - -func Test_group_odd_even(t *testing.T) { - - var ( - even = func(v int) (bool, error) { return v%2 == 0, nil } - groups, _ = Of(loop.KeyValuee(loop.Of(1, 1, 2, 4, 3, 1), even, func(i int) (int, error) { return i, nil })) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} diff --git a/break/kv/loop/loop.go b/break/kv/loop/loop.go deleted file mode 100644 index 8878a704..00000000 --- a/break/kv/loop/loop.go +++ /dev/null @@ -1,60 +0,0 @@ -package loop - -// Loop is a function that returns the next key\value or ok==false if there are no more elements. -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.Seq2] -type Loop[K, V any] func() (key K, value V, ok bool, err error) - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. -func (next Loop[K, V]) Track(consumer func(K, V) error) error { - return Track(next, consumer) -} - -// First returns the first element that satisfies the condition of the 'predicate' function -func (next Loop[K, V]) First(predicate func(K, V) bool) (K, V, bool, error) { - return First(next, predicate) -} - -// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func (next Loop[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (K, V, error) { - return Reduce(next, merge) -} - -// ReduceOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func (next Loop[K, V]) ReduceOK(merge func(K, K, V, V) (K, V)) (K, V, bool, error) { - return ReduceOK(next, merge) -} - -// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func (next Loop[K, V]) Reducee(merge func(K, K, V, V) (K, V, error)) (K, V, error) { - return Reducee(next, merge) -} - -// ReduceeOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func (next Loop[K, V]) ReduceeOK(merge func(K, K, V, V) (K, V, error)) (K, V, bool, error) { - return ReduceeOK(next, merge) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (next Loop[K, V]) HasAny(predicate func(K, V) bool) (bool, error) { - return HasAny(next, predicate) -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[K, V]) Filt(filter func(K, V) (bool, error)) Loop[K, V] { - return Filt(next, filter) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[K, V]) Filter(filter func(K, V) bool) Loop[K, V] { - return Filter(next, filter) -} - -// Crank rertieves next key\value elements from the 'next' function, returns the function, element, successfully flag. -func (next Loop[K, V]) Crank() (Loop[K, V], K, V, bool, error) { - return Crank(next) -} diff --git a/break/kv/loop/test/api_test.go b/break/kv/loop/test/api_test.go deleted file mode 100644 index 68d10e8f..00000000 --- a/break/kv/loop/test/api_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package test - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" - - breakkvloop "github.com/m4gshm/gollections/break/kv/loop" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/k" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/slice" -) - -func Test_HasAny(t *testing.T) { - 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)) - - result, _ := breakkvloop.HasAny(kvl, func(key int, _ string) bool { return key == 2 }) - - assert.True(t, result) -} - -func Test_HasAnyy(t *testing.T) { - 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)) - - result, _ := breakkvloop.HasAnyy(kvl, func(key int, _ string) (bool, error) { return key == 2, nil }) - - assert.True(t, result) -} - -func Test_Firstt(t *testing.T) { - 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)) - - k, v, ok, _ := breakkvloop.Firstt(kvl, func(key int, val string) (bool, error) { return key == 2 || val == "three", nil }) - - assert.True(t, ok) - assert.Equal(t, 2, k) - assert.Equal(t, "two", v) -} - -func Test_Reduce(t *testing.T) { - 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)) - - k, v, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.True(t, ok) - assert.Equal(t, 1+2+3, k) - assert.Equal(t, "one"+"two"+"three", v) -} - -func Test_Reduce_Empty(t *testing.T) { - kvl := breakkvloop.From(loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value)) - - _, _, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.False(t, ok) -} - -func Test_Reduce_Nil(t *testing.T) { - var l loop.Loop[c.KV[int, string]] - kvl := breakkvloop.From(loop.KeyValue(l, c.KV[int, string].Key, c.KV[int, string].Value)) - - _, _, ok, _ := breakkvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.False(t, ok) -} - -func Test_Reducee(t *testing.T) { - 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)) - - k, v, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.True(t, ok) - assert.Equal(t, 1+2+3, k) - assert.Equal(t, "one"+"two"+"three", v) -} - -func Test_Reducee_Empty(t *testing.T) { - kvl := breakkvloop.From(loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value)) - - _, _, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.False(t, ok) -} - -func Test_Reducee_Nil(t *testing.T) { - var l loop.Loop[c.KV[int, string]] - kvl := breakkvloop.From(loop.KeyValue(l, c.KV[int, string].Key, c.KV[int, string].Value)) - - _, _, ok, _ := breakkvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.False(t, ok) -} - -func Test_Convert(t *testing.T) { - 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)) - - out, _ := breakkvloop.Slice(breakkvloop.Convert(kvl, func(k int, v string) (int, string) { return k * k, v + v }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "11"), k.V(4, "22"), k.V(9, "33")), out) -} - -func Test_Conv(t *testing.T) { - 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)) - - out, _ := breakkvloop.Slice(breakkvloop.Conv(kvl, func(k int, v string) (int, string, error) { return k * k, v + v, nil }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "11"), k.V(4, "22"), k.V(9, "33")), out) -} - -func Test_Filter(t *testing.T) { - 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)) - - out, _ := breakkvloop.Slice(breakkvloop.Filter(kvl, func(key int, _ string) bool { return key != 2 }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(3, "3")), out) -} - -func Test_Filt(t *testing.T) { - 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)) - - out, _ := breakkvloop.Slice(breakkvloop.Filt(kvl, func(key int, _ string) (bool, error) { return key != 2, nil }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(3, "3")), out) -} - -func Test_Filt2(t *testing.T) { - 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)) - - out, err := breakkvloop.Slice(breakkvloop.Filt(kvl, func(key int, _ string) (bool, error) { - ok := key <= 2 - return ok, op.IfElse(key == 2, errors.New("abort"), nil) - }), k.V[int, string]) - - assert.Error(t, err) - assert.Equal(t, slice.Of(k.V(1, "1")), out) -} - -func Test_To(t *testing.T) { - bkvl := 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)) - - kvl := breakkvloop.To(bkvl, func(err error) { assert.NoError(t, err) }) - - out := kvloop.Slice(kvloop.Filter(kvl, func(key int, _ string) bool { return key != 2 }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(3, "3")), out) -} diff --git a/break/loop/api.go b/break/loop/api.go deleted file mode 100644 index bda91937..00000000 --- a/break/loop/api.go +++ /dev/null @@ -1,922 +0,0 @@ -// Package loop provides helpers for loop operation and iterator implementations -// -// Deprecated: use the [github.com/m4gshm/gollections/seq], [github.com/m4gshm/gollections/seqe], [github.com/m4gshm/gollections/seq2] packages API instead. -package loop - -import ( - "errors" - "unsafe" - - breakkvloop "github.com/m4gshm/gollections/break/kv/loop" - "github.com/m4gshm/gollections/break/predicate/always" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/convert" - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/map_/resolv" - "github.com/m4gshm/gollections/notsafe" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/op/check/not" -) - -// Break is the 'break' statement of the For, Track methods. -var Break = c.Break - -// S wrap the elements by loop function. -func S[TS ~[]T, T any](elements TS) Loop[T] { - return Of(elements...) -} - -// Of wrap the elements by loop function. -func Of[T any](elements ...T) func() (e T, ok bool, err error) { - l := len(elements) - i := 0 - if l == 0 || i < 0 || i >= l { - return func() (e T, ok bool, err error) { return e, false, nil } - } - return func() (e T, ok bool, err error) { - if i < l { - e, ok = elements[i], true - i++ - } - return e, ok, nil - } -} - -// New is the main breakable loop constructor -func New[S, T any](source S, hasNext func(S) bool, getNext func(S) (T, error)) Loop[T] { - return func() (out T, ok bool, err error) { - if ok := hasNext(source); !ok { - return out, false, nil - } - out, err = getNext(source) - return out, err == nil, err - } -} - -// From wrap the next loop to a breakable loop -func From[T any](next func() (T, bool)) Loop[T] { - if next == nil { - return nil - } - return func() (T, bool, error) { - e, ok := next() - return e, ok, nil - } -} - -// To transforms a breakable loop to a simple loop. -// The errConsumer is a function that is called when an error occurs. -func To[T any](next func() (T, bool, error), errConsumer func(error)) func() (T, bool) { - if next == nil { - return nil - } - return func() (T, bool) { - e, ok, err := next() - if err != nil { - errConsumer(err) - return e, false - } - return e, ok - } -} - -// All is an adapter for the next function for iterating by `for ... range`. -func All[T any](next func() (T, bool, error), consumer func(T, error) bool) { - if next == nil { - return - } - for { - v, ok, err := next() - if !ok { - if err != nil { - consumer(v, err) - } - break - } else { - consumer(v, err) - } - } -} - -// For applies the 'consumer' function for the elements retrieved by the 'next' function until the consumer returns the c.Break to stop. -func For[T any](next func() (T, bool, error), consumer func(T) error) error { - if next == nil { - return nil - } - for { - if v, ok, err := next(); err != nil || !ok { - return err - } else if err := consumer(v); err != nil { - return brk(err) - } - } -} - -// ForFiltered applies the 'consumer' function to the elements retrieved by the 'next' function that satisfy the 'predicate' function condition -func ForFiltered[T any](next func() (T, bool, error), consumer func(T) error, predicate func(T) bool) error { - if next == nil { - return nil - } - for { - if v, ok, err := next(); err != nil || !ok { - return err - } else if ok := predicate(v); ok { - if err := consumer(v); err != nil { - return brk(err) - } - } - } -} - -// First returns the first element that satisfies the condition of the 'predicate' function -func First[T any](next func() (T, bool, error), predicate func(T) bool) (out T, ok bool, err error) { - if next == nil { - return out, false, nil - } - for { - if out, ok, err = next(); err != nil || !ok { - return out, false, err - } else if ok := predicate(out); ok { - return out, true, nil - } - } -} - -// Firstt returns the first element that satisfies the condition of the 'predicate' function -func Firstt[T any](next func() (T, bool, error), predicate func(T) (bool, error)) (out T, ok bool, err error) { - if next == nil { - return out, false, nil - } - for { - if out, ok, err := next(); err != nil || !ok { - return out, false, err - } else if ok, err := predicate(out); err != nil || ok { - return out, ok, err - } - } -} - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. -func Track[I, T any](next func() (I, T, bool, error), consumer func(I, T) error) error { - return breakkvloop.Track(next, consumer) -} - -// Slice collects the elements retrieved by the 'next' function into a slice -func Slice[T any](next func() (T, bool, error)) (out []T, err error) { - if next == nil { - return nil, nil - } - for { - v, ok, err := next() - if ok { - out = append(out, v) - } - if !ok || err != nil { - return out, err - } - } -} - -// SliceCap collects the elements retrieved by the 'next' function into a new slice with predefined capacity -func SliceCap[T any](next func() (T, bool, error), capacity int) (out []T, err error) { - if next == nil { - return nil, nil - } - if capacity > 0 { - out = make([]T, 0, capacity) - } - return Append(next, out) -} - -// Append collects the elements retrieved by the 'next' function into the specified 'out' slice -func Append[T any, TS ~[]T](next func() (T, bool, error), out TS) (TS, error) { - if next == nil { - return out, nil - } - for v, ok, err := next(); ok; v, ok, err = next() { - if err != nil { - return out, err - } - out = append(out, v) - } - return out, nil -} - -// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func Reduce[T any](next func() (T, bool, error), merge func(T, T) T) (T, error) { - result, _, err := ReduceOK(next, merge) - return result, err -} - -// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceOK[T any](next func() (T, bool, error), merge func(T, T) T) (result T, ok bool, err error) { - if next == nil { - return result, false, nil - } - if result, ok, err = next(); err != nil || !ok { - return result, ok, err - } - result, err = Accum(result, next, merge) - return result, true, err -} - -// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func Reducee[T any](next func() (T, bool, error), merge func(T, T) (T, error)) (T, error) { - result, _, err := ReduceeOK(next, merge) - return result, err -} - -// ReduceeOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceeOK[T any](next func() (T, bool, error), merge func(T, T) (T, error)) (result T, ok bool, err error) { - if next == nil { - return result, false, nil - } - if result, ok, err = next(); err != nil || !ok { - return result, ok, err - } - result, err = Accumm(result, next, merge) - return result, true, err -} - -// 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. -func Accum[T any](first T, next func() (T, bool, error), merge func(T, T) T) (accumulator T, err error) { - accumulator = first - if next == nil { - return accumulator, nil - } - for { - v, ok, err := next() - if err != nil { - return accumulator, err - } else if !ok { - return accumulator, nil - } - accumulator = merge(accumulator, v) - } -} - -// 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. -func Accumm[T any](first T, next func() (T, bool, error), merge func(T, T) (T, error)) (accumulator T, err error) { - accumulator = first - if next == nil { - return accumulator, nil - } - for { - if v, ok, err := next(); err != nil || !ok { - return accumulator, err - } else if accumulator, err = merge(accumulator, v); err != nil { - return accumulator, err - } - } -} - -// Sum returns the sum of all elements -func Sum[T c.Summable](next func() (T, bool, error)) (T, error) { - return Reduce(next, op.Sum[T]) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func HasAny[T any](next func() (T, bool, error), predicate func(T) bool) (bool, error) { - _, ok, err := First(next, predicate) - return ok, err -} - -// HasAnyy finds the first element that satisfies the 'predicate' function condition and returns true if successful -func HasAnyy[T any](next func() (T, bool, error), predicate func(T) (bool, error)) (bool, error) { - _, ok, err := Firstt(next, predicate) - return ok, err -} - -// Contains finds the first element that equal to the example and returns true -func Contains[T comparable](next func() (T, bool, error), example T) (bool, error) { - if next == nil { - return false, nil - } - for { - if one, ok, err := next(); err != nil || !ok { - return false, err - } else if one == example { - return true, nil - } - } -} - -// Conv creates a loop that applies the 'converter' function to iterable elements. -func Conv[From, To any](next func() (From, bool, error), converter func(From) (To, error)) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - v, ok, err := next() - if err != nil || !ok { - return t, false, err - } - vc, err := converter(v) - return vc, err == nil, err - } -} - -// Convert creates a loop that applies the 'converter' function to iterable elements. -func Convert[From, To any](next func() (From, bool, error), converter func(From) To) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - if v, ok, err := next(); err != nil || !ok { - return t, ok, err - } else { - return converter(v), true, nil - } - } -} - -// ConvOK creates a loop that applies the 'converter' function to iterable elements. -// The converter may returns converted value or ok=false to exclude the value from the loop. -// It may also return an error to abort the loop. -func ConvOK[From, To any](next func() (From, bool, error), converter func(from From) (to To, ok bool, err error)) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - for { - if v, ok, err := next(); err != nil || !ok { - return t, false, err - } else if vc, ok, err := converter(v); err != nil || ok { - return vc, ok, err - } - } - } -} - -// ConvertOK creates a loop that applies the 'converter' function to iterable elements. -// The converter may returns a value or ok=false to exclude the value from the loop. -func ConvertOK[From, To any](next func() (From, bool, error), converter func(from From) (To, bool)) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - for { - if e, ok, err := next(); err != nil || !ok { - return t, false, err - } else if t, ok := converter(e); ok { - return t, ok, err - } - } - } -} - -// FiltAndConv creates a loop that filters source elements and converts them -func FiltAndConv[From, To any](next func() (From, bool, error), filter func(From) (bool, error), converter func(From) (To, error)) Loop[To] { - return FilterConvertFilter(next, filter, converter, always.True[To]) -} - -// FilterAndConvert creates a loop that filters source elements and converts them -func FilterAndConvert[From, To any](next func() (From, bool, error), filter func(From) bool, converter func(From) To) Loop[To] { - return FilterConvertFilter(next, func(f From) (bool, error) { return filter(f), nil }, func(f From) (To, error) { return converter(f), nil }, always.True[To]) -} - -// FilterConvertFilter filters source, converts, and filters converted elements -func FilterConvertFilter[From, To any](next func() (From, bool, error), filter func(From) (bool, error), converter func(From) (To, error), filterTo func(To) (bool, error)) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - for { - if f, ok, err := Firstt(next, filter); err != nil || !ok { - return t, false, err - } else if cf, err := converter(f); err != nil { - return t, false, err - } else if ok, err := filterTo(cf); err != nil || !ok { - return t, false, err - } else { - return cf, true, nil - } - } - } -} - -// ConvertAndFilter additionally filters 'To' elements -func ConvertAndFilter[From, To any](next func() (From, bool, error), converter func(From) (To, error), filter func(To) (bool, error)) Loop[To] { - return FilterConvertFilter(next, always.True[From], converter, filter) -} - -// Flatt converts a two-dimensional loop in an one-dimensional one. -func Flatt[From, To any](next func() (From, bool, error), flattener func(From) ([]To, error)) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool, err error) { - if sizeTo > 0 { - if indexTo < sizeTo { - indexTo++ - return *(*To)(notsafe.GetArrayElemRef(arrayTo, indexTo, elemSizeTo)), true, nil - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - for { - if v, ok, err := next(); err != nil || !ok { - return t, ok, err - } else if elementsTo, err := flattener(v); err != nil { - return t, false, err - } else if len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - return *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)), true, nil - } - } - } -} - -// Flat converts a two-dimensional loop in an one-dimensional one. -func Flat[From, To any](next func() (From, bool, error), flattener func(From) []To) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool, err error) { - if sizeTo > 0 { - if indexTo < sizeTo { - i := indexTo - indexTo++ - return *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)), true, nil - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - for { - if v, ok, err := next(); err != nil { - return t, false, err - } else if !ok { - return t, false, nil - } else if elementsTo := flattener(v); len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - return *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)), true, nil - } - } - } -} - -// FiltAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function -func FiltAndFlat[From, To any](next func() (From, bool, error), filter func(From) (bool, error), flattener func(From) ([]To, error)) Loop[To] { - return FiltFlattFilt(next, filter, flattener, always.True[To]) -} - -// FilterAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function -func FilterAndFlat[From, To any](next func() (From, bool, error), filter func(From) bool, flattener func(From) []To) Loop[To] { - return FiltFlattFilt(next, func(f From) (bool, error) { return filter(f), nil }, func(f From) ([]To, error) { return flattener(f), nil }, always.True[To]) -} - -// FlatAndFilt extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FlatAndFilt[From, To any](next func() (From, bool, error), flattener func(From) ([]To, error), filterTo func(To) (bool, error)) Loop[To] { - return FiltFlattFilt(next, always.True[From], flattener, filterTo) -} - -// FlattAndFilter extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FlattAndFilter[From, To any](next func() (From, bool, error), flattener func(From) []To, filterTo func(To) bool) Loop[To] { - return FiltFlattFilt(next, always.True[From], func(f From) ([]To, error) { return flattener(f), nil }, func(t To) (bool, error) { return filterTo(t), nil }) -} - -// FiltFlattFilt filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FiltFlattFilt[From, To any](next func() (From, bool, error), filterFrom func(From) (bool, error), flattener func(From) ([]To, error), filterTo func(To) (bool, error)) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool, err error) { - for { - if sizeTo > 0 { - if indexTo < sizeTo { - i := indexTo - indexTo++ - t = *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)) - if ok, err := filterTo(t); err != nil { - return t, false, err - } else if ok { - return t, true, nil - } - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - - if v, ok, err := next(); err != nil || !ok { - return t, false, err - } else if ok, err := filterFrom(v); err != nil { - return t, false, err - } else if ok { - if elementsTo, err := flattener(v); err != nil { - return t, false, err - } else if len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - t = *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)) - if ok, err := filterTo(t); err != nil || ok { - return t, ok, err - } - } - } - } - } - // return &FlattFiltIter[From, To]{next: next, filterFrom: filterFrom, flattener: flattener, filterTo: filterTo, elemSizeTo: notsafe.GetTypeSize[To]()} -} - -// FilterFlatFilter filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FilterFlatFilter[From, To any](next func() (From, bool, error), filterFrom func(From) bool, flattener func(From) []To, filterTo func(To) bool) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool, err error) { - for { - if sizeTo > 0 { - if indexTo < sizeTo { - i := indexTo - indexTo++ - tv := *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)) - if ok := filterTo(tv); ok { - return tv, true, nil - } - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - - if fv, ok, err := next(); err != nil || !ok { - return t, false, err - } else if ok := filterFrom(fv); ok { - if elementsTo := flattener(fv); len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - tv := *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)) - if ok := filterTo(tv); ok { - return tv, true, nil - } - } - } - } - } -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filt[T any](next func() (T, bool, error), filter func(T) (bool, error)) Loop[T] { - if next == nil { - return nil - } - return func() (T, bool, error) { - t, ok, err := Firstt(next, filter) - return t, ok && err == nil, err - } -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filter[T any](next func() (T, bool, error), filter func(T) bool) Loop[T] { - if next == nil { - return nil - } - return func() (T, bool, error) { - return First(next, filter) - } -} - -// NotNil creates a loop that filters nullable elements. -func NotNil[T any](next func() (*T, bool, error)) Loop[*T] { - return Filt(next, as.ErrTail(not.Nil[T])) -} - -// PtrVal creates a loop that transform pointers to the values referenced by those pointers. -// Nil pointers are transformet to zero values. -func PtrVal[T any](next func() (*T, bool, error)) Loop[T] { - return Convert(next, convert.PtrVal[T]) -} - -// NoNilPtrVal creates a loop that transform only not nil pointers to the values referenced referenced by those pointers. -// Nil pointers are ignored. -func NoNilPtrVal[T any](next func() (*T, bool, error)) Loop[T] { - return ConvertOK(next, convert.NoNilPtrVal[T]) -} - -// KeyValue transforms a loop to the key/value loop based on applying key, value extractors to the elements -func KeyValue[T any, K, V any](next func() (T, bool, error), keyExtractor func(T) K, valExtractor func(T) V) breakkvloop.Loop[K, V] { - return KeyValuee(next, as.ErrTail(keyExtractor), as.ErrTail(valExtractor)) -} - -// KeyValuee transforms a loop to the key/value loop based on applying key, value extractors to the elements -func KeyValuee[T any, K, V any](next func() (T, bool, error), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) breakkvloop.Loop[K, V] { - if next == nil { - return nil - } - return func() (key K, value V, ok bool, err error) { - if elem, nextOk, err := next(); err != nil || !nextOk { - return key, value, false, err - } else if key, err = keyExtractor(elem); err == nil { - value, err = valExtractor(elem) - return key, value, true, err - } - return key, value, false, nil - } -} - -// KeysValues transforms a loop to the key/value loop based on applying multiple keys, values extractor to the elements -func KeysValues[T, K, V any](next func() (T, bool, error), keysExtractor func(T) ([]K, error), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[K, V] { - if next == nil { - return nil - } - var ( - keys []K - values []V - ki, vi int - ) - return func() (key K, value V, ok bool, err error) { - for !ok { - var ( - keysLen, valuesLen = len(keys), len(values) - lastKeyIndex, lastValIndex = keysLen - 1, valuesLen - 1 - ) - if keysLen > 0 && ki >= 0 && ki <= lastKeyIndex { - key = keys[ki] - ok = true - } - if valuesLen > 0 && vi >= 0 && vi <= lastValIndex { - value = values[vi] - ok = true - } - if ok { - if ki < lastKeyIndex { - ki++ - } else if vi < lastValIndex { - ki = 0 - vi++ - } else { - keys, values = nil, nil - } - } else if elem, nextOk, err := next(); err != nil { - return key, value, ok, err - } else if nextOk { - keys, err = keysExtractor(elem) - if err == nil { - values, err = valsExtractor(elem) - } - if err != nil { - break - } - ki, vi = 0, 0 - } else { - keys, values = nil, nil - break - } - } - return key, value, ok, nil - } - // return NewMultipleKeyValuer(next, keysExtractor, valsExtractor) -} - -// KeysValue transforms a loop to the key/value loop based on applying key, value extractor to the elements -func KeysValue[T, K, V any](next func() (T, bool, error), keysExtractor func(T) []K, valExtractor func(T) V) breakkvloop.Loop[K, V] { - return KeysValues(next, as.ErrTail(keysExtractor), convSlice(as.ErrTail(valExtractor))) -} - -// KeysValuee transforms a loop to the key/value loop based on applying key, value extractor to the elements -func KeysValuee[T, K, V any](next func() (T, bool, error), keysExtractor func(T) ([]K, error), valExtractor func(T) (V, error)) breakkvloop.Loop[K, V] { - return KeysValues(next, keysExtractor, convSlice(valExtractor)) -} - -// KeyValues transforms a loop to the key/value loop based on applying key, value extractor to the elements -func KeyValues[T, K, V any](next func() (T, bool, error), keyExtractor func(T) K, valsExtractor func(T) []V) breakkvloop.Loop[K, V] { - return KeysValues(next, convSlice(as.ErrTail(keyExtractor)), as.ErrTail(valsExtractor)) -} - -// KeyValuess transforms a loop to the key/value loop based on applying key, value extractor to the elements -func KeyValuess[T, K, V any](next func() (T, bool, error), keyExtractor func(T) (K, error), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[K, V] { - return KeysValues(next, convSlice(keyExtractor), valsExtractor) -} - -// ExtraVals transforms a loop to the key/value loop based on applying value extractor to the elements -func ExtraVals[T, V any](next func() (T, bool, error), valsExtractor func(T) []V) breakkvloop.Loop[T, V] { - return KeyValues(next, as.Is[T], valsExtractor) -} - -// ExtraValss transforms a loop to the key/value loop based on applying values extractor to the elements -func ExtraValss[T, V any](next func() (T, bool, error), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[T, V] { - return KeyValuess(next, as.ErrTail(as.Is[T]), valsExtractor) -} - -// ExtraKeys transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeys[T, K any](next func() (T, bool, error), keysExtractor func(T) []K) breakkvloop.Loop[K, T] { - return KeysValue(next, keysExtractor, as.Is[T]) -} - -// ExtraKeyss transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeyss[T, K any](next func() (T, bool, error), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { - return KeyValuess(next, keyExtractor, as.ErrTail(convert.AsSlice[T])) -} - -// ExtraKey transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKey[T, K any](next func() (T, bool, error), keysExtractor func(T) K) breakkvloop.Loop[K, T] { - return KeyValue(next, keysExtractor, as.Is[T]) -} - -// ExtraKeyy transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeyy[T, K any](next func() (T, bool, error), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { - return KeyValuee[T, K](next, keyExtractor, as.ErrTail(as.Is[T])) -} - -// ExtraValue transforms a loop to the key/value loop based on applying value extractor to the elements -func ExtraValue[T, V any](next func() (T, bool, error), valueExtractor func(T) V) breakkvloop.Loop[T, V] { - return KeyValue(next, as.Is[T], valueExtractor) -} - -// ExtraValuee transforms a loop to the key/value loop based on applying value extractor to the elements -func ExtraValuee[T, V any](next func() (T, bool, error), valExtractor func(T) (V, error)) breakkvloop.Loop[T, V] { - return KeyValuee[T, T, V](next, as.ErrTail(as.Is[T]), valExtractor) -} - -// Group converts elements retrieved by the 'next' function into a new map, extracting a key for each element applying the converter 'keyExtractor'. -// The keyExtractor converts an element to a key. -// The valExtractor converts an element to a value. -func Group[T any, K comparable, V any](next func() (T, bool, error), keyExtractor func(T) K, valExtractor func(T) V) (map[K][]V, error) { - return Groupp(next, as.ErrTail(keyExtractor), as.ErrTail(valExtractor)) -} - -// Groupp converts elements retrieved by the 'next' function into a new map, extracting a key for each element applying the converter 'keyExtractor'. -// The keyExtractor converts an element to a key. -// The valExtractor converts an element to a value. -func Groupp[T any, K comparable, V any](next func() (T, bool, error), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) (map[K][]V, error) { - return MapResolvv(next, keyExtractor, valExtractor, func(ok bool, k K, rv []V, v V) ([]V, error) { - return resolv.Slice(ok, k, rv, v), nil - }) -} - -// GroupByMultiple converts elements retrieved by the 'next' function into a new map, extracting multiple keys, values per each element applying the 'keysExtractor' and 'valsExtractor' functions. -// The keysExtractor retrieves one or more keys per element. -// The valsExtractor retrieves one or more values per element. -func GroupByMultiple[T any, K comparable, V any](next func() (T, bool, error), keysExtractor func(T) []K, valsExtractor func(T) []V) (map[K][]V, error) { - groups := map[K][]V{} - for { - if e, ok, err := next(); err != nil || !ok { - return groups, err - } else if keys, vals := keysExtractor(e), valsExtractor(e); len(keys) == 0 { - var key K - for _, v := range vals { - initGroup(key, v, groups) - } - } else { - for _, key := range keys { - if len(vals) == 0 { - var v V - initGroup(key, v, groups) - } else { - for _, v := range vals { - initGroup(key, v, groups) - } - } - } - } - } -} - -// GroupByMultipleKeys converts elements retrieved by the 'next' function into a new map, extracting multiple keys, one value per each element applying the 'keysExtractor' and 'valExtractor' functions. -// The keysExtractor retrieves one or more keys per element. -// The valExtractor converts an element to a value. -func GroupByMultipleKeys[T any, K comparable, V any](next func() (T, bool, error), keysExtractor func(T) []K, valExtractor func(T) V) (map[K][]V, error) { - groups := map[K][]V{} - for { - if e, ok, err := next(); err != nil || !ok { - return groups, err - } else if keys, v := keysExtractor(e), valExtractor(e); len(keys) == 0 { - var key K - initGroup(key, v, groups) - } else { - for _, key := range keys { - initGroup(key, v, groups) - } - } - } -} - -// GroupByMultipleValues converts elements retrieved by the 'next' function into a new map, extracting one key, multiple values per each element applying the 'keyExtractor' and 'valsExtractor' functions. -// The keyExtractor converts an element to a key. -// The valsExtractor retrieves one or more values per element. -func GroupByMultipleValues[T any, K comparable, V any](next func() (T, bool, error), keyExtractor func(T) K, valsExtractor func(T) []V) (map[K][]V, error) { - groups := map[K][]V{} - for { - if e, ok, err := next(); err != nil || !ok { - return groups, err - } else if key, vals := keyExtractor(e), valsExtractor(e); len(vals) == 0 { - var v V - initGroup(key, v, groups) - } else { - for _, v := range vals { - initGroup(key, v, groups) - } - } - } -} - -func initGroup[T any, K comparable, TS ~[]T](key K, e T, groups map[K]TS) { - groups[key] = append(groups[key], e) -} - -// Map collects key\value elements into a new map by iterating over the elements -func Map[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) (map[K]V, error) { - return Mapp(From(next), as.ErrTail(keyExtractor), as.ErrTail(valExtractor)) -} - -// Mapp collects key\value elements into a new map by iterating over the elements -func Mapp[T any, K comparable, V any](next func() (T, bool, error), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) (map[K]V, error) { - return MapResolvv(next, keyExtractor, valExtractor, func(ok bool, k K, rv V, v V) (V, error) { return resolv.First(ok, k, rv, v), nil }) -} - -// MapResolvv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values -func MapResolvv[T any, K comparable, V, VR any]( - next func() (T, bool, error), keyExtractor func(T) (K, error), valExtractor func(T) (V, error), - resolver func(bool, K, VR, V) (VR, error), -) (m map[K]VR, err error) { - if next == nil { - return nil, nil - } - m = map[K]VR{} - for { - if e, ok, err := next(); err != nil || !ok { - return m, err - } else if k, err := keyExtractor(e); err != nil { - return m, err - } else if v, err := valExtractor(e); err != nil { - return m, err - } else { - exists, ok := m[k] - if m[k], err = resolver(ok, k, exists, v); err != nil { - return m, err - } - } - } -} - -// ConvertAndReduce converts each elements and merges them into one -func ConvertAndReduce[From, To any](next func() (From, bool, error), converter func(From) To, merge func(To, To) To) (out To, err error) { - return Reduce(Convert(next, converter), merge) -} - -// ConvAndReduce converts each elements and merges them into one -func ConvAndReduce[From, To any](next func() (From, bool, error), converter func(From) (To, error), merge func(To, To) To) (out To, err error) { - return Reduce(Conv(next, converter), merge) -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func Crank[T any](next func() (T, bool, error)) (n Loop[T], t T, ok bool, err error) { - if next != nil { - t, ok, err = next() - } - return next, t, ok, err -} - -func brk(err error) error { - if errors.Is(err, c.Break) { - return nil - } - return err -} - -func convSlice[T, V any](conv func(T) (V, error)) func(t T) ([]V, error) { - return func(t T) ([]V, error) { - v, err := conv(t) - if err != nil { - return nil, err - } - return convert.AsSlice(v), nil - } -} diff --git a/break/loop/loop.go b/break/loop/loop.go deleted file mode 100644 index 22dd4f70..00000000 --- a/break/loop/loop.go +++ /dev/null @@ -1,85 +0,0 @@ -package loop - -// Loop is a function that returns the next element, ok==false if there are no more elements or an error if something is wrong. -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.SeqE] -type Loop[T any] func() (element T, ok bool, err error) - -// All is used to iterate through the loop using `for ... range`. -func (next Loop[T]) All(consumer func(T, error) bool) { - All(next, consumer) -} - -// For applies the 'consumer' function for the elements retrieved by the 'next' function until the consumer returns the c.Break to stop. -func (next Loop[T]) For(consumer func(T) error) error { - return For(next, consumer) -} - -// First returns the first element that satisfies the condition of the 'predicate' function -func (next Loop[T]) First(predicate func(T) bool) (T, bool, error) { - return First(next, predicate) -} - -// Slice collects the elements retrieved by the 'next' function into a new slice -func (next Loop[T]) Slice() ([]T, error) { - return Slice(next) -} - -// SliceCap collects the elements retrieved by the 'next' function into a new slice with predefined capacity -func (next Loop[T]) SliceCap(capacity int) ([]T, error) { - return SliceCap(next, capacity) -} - -// Append collects the elements retrieved by the 'next' function into the specified 'out' slice -func (next Loop[T]) Append(out []T) ([]T, error) { - return Append(next, out) -} - -// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func (next Loop[T]) Reduce(merge func(T, T) T) (T, error) { - return Reduce(next, merge) -} - -// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func (next Loop[T]) ReduceOK(merge func(T, T) T) (result T, ok bool, err error) { - return ReduceOK(next, merge) -} - -// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func (next Loop[T]) Reducee(merge func(T, T) (T, error)) (T, error) { - return Reducee(next, merge) -} - -// ReduceeOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func (next Loop[T]) ReduceeOK(merge func(T, T) (T, error)) (result T, ok bool, err error) { - return ReduceeOK(next, merge) -} - -// 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. -func (next Loop[T]) Accum(first T, merge func(T, T) T) (T, error) { - return Accum(first, next, merge) -} - -// 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. -func (next Loop[T]) Accumm(first T, merge func(T, T) (T, error)) (T, error) { - return Accumm(first, next, merge) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (next Loop[T]) HasAny(predicate func(T) bool) (bool, error) { - return HasAny(next, predicate) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[T]) Filter(filter func(T) bool) Loop[T] { - return Filter(next, filter) -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func (next Loop[T]) Crank() (Loop[T], T, bool, error) { - return Crank(next) -} diff --git a/break/loop/test/api_go_1_22_test.go b/break/loop/test/api_go_1_22_test.go deleted file mode 100644 index 76d8bb76..00000000 --- a/break/loop/test/api_go_1_22_test.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build goexperiment.rangefunc - -package test - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/break/loop" -) - -func Test_IterAll(t *testing.T) { - var ( - r []int - rerr error - ) - for v, err := range loop.Conv(loop.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi).All { - if rerr = err; err == nil { - r = append(r, v) - } - } - - assert.Equal(t, []int{1, 3, 5}, r) - assert.Error(t, rerr, "invalid syntax") -} diff --git a/break/loop/test/api_test.go b/break/loop/test/api_test.go deleted file mode 100644 index e0b1fbb3..00000000 --- a/break/loop/test/api_test.go +++ /dev/null @@ -1,387 +0,0 @@ -package test - -import ( - "errors" - "strconv" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - breakKvLoop "github.com/m4gshm/gollections/break/kv/loop" - breakLoop "github.com/m4gshm/gollections/break/loop" - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/convert" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/predicate/eq" - "github.com/m4gshm/gollections/predicate/more" - "github.com/m4gshm/gollections/slice" -) - -func Test_AccumSum(t *testing.T) { - s := breakLoop.Of(1, 3, 5, 7, 9, 11) - r, err := breakLoop.Accum(100, s, op.Sum[int]) - assert.Equal(t, 100+1+3+5+7+9+11, r) - assert.NoError(t, err) -} - -func Test_AccummSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, err := loop.Accumm(100, s, func(i1, i2 int) (int, error) { - if i2 == 11 { - return i1, errors.New("stop") - } - return i1 + i2, nil - }) - assert.Equal(t, 100+1+3+5+7+9, r) - assert.ErrorContains(t, err, "stop") -} - -func Test_ReduceSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok, err := breakLoop.ReduceOK(breakLoop.From(s), op.Sum[int]) - assert.NoError(t, err) - assert.True(t, ok) - assert.Equal(t, 1+3+5+7+9+11, r) -} - -func Test_ReduceeSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok, err := breakLoop.ReduceeOK(breakLoop.From(s), func(i1, i2 int) (int, error) { - if i2 == 11 { - return i1, errors.New("stop") - } - return i1 + i2, nil - }) - assert.ErrorContains(t, err, "stop") - assert.True(t, ok) - assert.Equal(t, 1+3+5+7+9, r) -} - -func Test_ReduceeSumFirstErr(t *testing.T) { - var tru breakLoop.Loop[int] = func() (int, bool, error) { - return 1, true, errors.New("first-err") - } - r, ok, err := breakLoop.ReduceeOK(tru, func(i1, i2 int) (int, error) { - return i1 + i2, nil - }) - assert.ErrorContains(t, err, "first-err") - assert.True(t, ok) - assert.Equal(t, 1, r) - - var fals breakLoop.Loop[int] = func() (int, bool, error) { - return 2, false, errors.New("first-err") - } - - r, ok, err = breakLoop.ReduceeOK(fals, func(i1, i2 int) (int, error) { - return i1 + i2, nil - }) - assert.ErrorContains(t, err, "first-err") - assert.False(t, ok) - assert.Equal(t, 2, r) -} - -func Test_Firstt(t *testing.T) { - result, ok, err := loop.Firstt(loop.Of(1, 2, 3, 4, 5, 6), func(i int) (bool, error) { - return more.Than(5)(i), nil - }) - - assert.True(t, ok) - assert.Equal(t, 6, result) - assert.NoError(t, err) - - result, ok, err = loop.Firstt(loop.Of(1, 2, 3, 4, 5, 6), func(_ int) (bool, error) { return true, errors.New("abort") }) - - assert.True(t, ok) - assert.Equal(t, 1, result) - assert.ErrorContains(t, err, "abort") - - _, ok, err = loop.Firstt(loop.Of(1, 2, 3, 4, 5, 6), func(_ int) (bool, error) { return false, errors.New("abort") }) - - assert.False(t, ok) - assert.ErrorContains(t, err, "abort") - - // _, ok, _ = loop.Firstt(loop.Of(1, 2, 3, 4, 5, 6), nil) - // assert.False(t, ok) - - _, ok, _ = loop.Firstt(nil, func(_ int) (bool, error) { return false, errors.New("abort") }) - assert.False(t, ok) -} - -func Test_ReduceeEmptyLoop(t *testing.T) { - s := breakLoop.Of[int]() - r, ok, err := breakLoop.ReduceOK(s, op.Sum[int]) - assert.NoError(t, err) - assert.False(t, ok) - assert.Equal(t, 0, r) -} - -func Test_ReduceeNilLoop(t *testing.T) { - var s breakLoop.Loop[int] - r, ok, err := breakLoop.ReduceOK(s, op.Sum[int]) - assert.NoError(t, err) - assert.False(t, ok) - assert.Equal(t, 0, r) -} - -func Test_Sum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, _ := breakLoop.Sum(breakLoop.From(s)) - assert.Equal(t, 1+3+5+7+9+11, r) -} - -func Test_Convert(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r := breakLoop.Convert(breakLoop.From(s), strconv.Itoa) - o, _ := breakLoop.Slice(r) - assert.Equal(t, []string{"1", "3", "5", "7", "9", "11"}, o) -} - -func Test_IterWitErr(t *testing.T) { - s := breakLoop.From(loop.Of("1", "3", "5", "7eee", "9", "11")) - r := []int{} - var outErr error - for { - it := breakLoop.Conv(s, strconv.Atoi) - i, ok, err := it() - if err != nil { - assert.False(t, ok) - outErr = err - break - } - r = append(r, i) - } - - assert.Error(t, outErr) - assert.Equal(t, []int{1, 3, 5}, r) - - s = breakLoop.From(loop.Of("1", "3", "5", "7eee", "9", "11")) - r = []int{} - //ignore err - for { - it := breakLoop.Conv(s, strconv.Atoi) - i, ok, err := it() - if !ok && err == nil { - break - } - if err == nil { - r = append(r, i) - } - } - assert.Equal(t, []int{1, 3, 5, 9, 11}, r) -} - -func Test_NotNil(t *testing.T) { - type entity struct{ val string } - var ( - source = breakLoop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = breakLoop.NotNil(source) - expected = []*entity{{"first"}, {"third"}, {"fifth"}} - ) - o, _ := breakLoop.Slice(result) - assert.Equal(t, expected, o) -} - -func Test_ConvertPointersToValues(t *testing.T) { - type entity struct{ val string } - var ( - source = breakLoop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = breakLoop.PtrVal(source) - expected = []entity{{"first"}, {}, {"third"}, {}, {"fifth"}} - ) - o, _ := breakLoop.Slice(result) - assert.Equal(t, expected, o) -} - -func Test_ConvertNotnilPointersToValues(t *testing.T) { - type entity struct{ val string } - var ( - source = breakLoop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = breakLoop.NoNilPtrVal(source) - expected = []entity{{"first"}, {"third"}, {"fifth"}} - ) - o, _ := breakLoop.Slice(result) - assert.Equal(t, expected, o) -} - -func Test_ConvertNotNil(t *testing.T) { - type entity struct{ val string } - var ( - source = loop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = convert.NotNil(source, func(e *entity) string { return e.val }) - expected = []string{"first", "third", "fifth"} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertToNotNil(t *testing.T) { - type entity struct{ val *string } - var ( - first = "first" - third = "third" - fifth = "fifth" - source = loop.Of([]entity{{&first}, {}, {&third}, {}, {&fifth}}...) - result = convert.ToNotNil(source, func(e entity) *string { return e.val }) - expected = []*string{&first, &third, &fifth} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertNilSafe(t *testing.T) { - type entity struct{ val *string } - var ( - first = "first" - third = "third" - fifth = "fifth" - source = loop.Of([]*entity{{&first}, {}, {&third}, nil, {&fifth}}...) - result = convert.NilSafe(source, func(e *entity) *string { return e.val }) - expected = []*string{&first, &third, &fifth} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -var even = func(v int) bool { return v%2 == 0 } - -func Test_ConvertFiltered(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := breakLoop.FilterAndConvert(breakLoop.From(s), even, strconv.Itoa) - o, _ := breakLoop.Slice(r) - assert.Equal(t, []string{"4", "8"}, o) -} - -func Test_ConvertFilteredInplace(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := breakLoop.ConvOK(breakLoop.From(s), func(i int) (string, bool, error) { return strconv.Itoa(i), even(i), nil }) - o, _ := breakLoop.Slice(r) - assert.Equal(t, []string{"4", "8"}, o) -} - -func Test_Flatt(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := breakLoop.Flat(breakLoop.From(md), as.Is) - e := []int{1, 2, 3, 4, 5, 6} - o, _ := breakLoop.Slice(f) - assert.Equal(t, e, o) -} - -func Test_FlattFilter(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := breakLoop.FilterAndFlat(breakLoop.From(md), func(from []int) bool { return len(from) > 1 }, as.Is) - e := []int{1, 2, 3, 5, 6} - o, _ := breakLoop.Slice(f) - assert.Equal(t, e, o) -} - -func Test_FlattElemFilter(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := breakLoop.FlattAndFilter(breakLoop.From(md), as.Is, even) - e := []int{2, 4, 6} - o, _ := breakLoop.Slice(f) - assert.Equal(t, e, o) -} - -func Test_FilterAndFlattFilt(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := breakLoop.FilterFlatFilter(breakLoop.From(md), func(from []int) bool { return len(from) > 1 }, as.Is, even) - e := []int{2, 6} - o, _ := breakLoop.Slice(f) - assert.Equal(t, e, o) -} - -func Test_Filter(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - f := breakLoop.Filter(breakLoop.From(s), even) - e := []int{4, 8} - o, _ := breakLoop.Slice(f) - assert.Equal(t, e, o) -} - -func Test_Filtering(t *testing.T) { - r := breakLoop.Filt(breakLoop.From(loop.Of(1, 2, 3, 4, 5, 6)), func(i int) (bool, error) { return i%2 == 0, nil }) - o, _ := breakLoop.Slice(r) - assert.Equal(t, []int{2, 4, 6}, o) -} - -func Test_MatchAny(t *testing.T) { - elements := loop.Of(1, 2, 3, 4) - - ok, _ := breakLoop.HasAny(breakLoop.From(elements), eq.To(4)) - assert.True(t, ok) - - noOk, _ := breakLoop.HasAny(breakLoop.From(elements), more.Than(5)) - assert.False(t, noOk) -} - -type Role struct { - name string -} - -type User struct { - name string - age int - roles []Role -} - -func (u User) Name() string { return u.name } -func (u User) Age() int { return u.age } -func (u User) Roles() []Role { return u.roles } - -var users = []User{ - {name: "Bob", age: 26, roles: []Role{{"Admin"}, {"manager"}}}, - {name: "Alice", age: 35, roles: []Role{{"Manager"}}}, - {name: "Tom", age: 18}, {}, -} - -func Test_KeyValuer(t *testing.T) { - m, _ := breakKvLoop.Group(breakLoop.KeyValue(breakLoop.From(loop.Of(users...)), User.Name, User.Age)) - - assert.Equal(t, m["Alice"], slice.Of(35)) - assert.Equal(t, m["Bob"], slice.Of(26)) - assert.Equal(t, m["Tom"], slice.Of(18)) - - g, _ := breakLoop.Group(breakLoop.From(loop.Of(users...)), User.Name, User.Age) - assert.Equal(t, m, g) -} - -func Test_Keyer(t *testing.T) { - m, _ := breakKvLoop.Group(breakLoop.ExtraKey(breakLoop.From(loop.Of(users...)), User.Name)) - - assert.Equal(t, m["Alice"], slice.Of(users[1])) - assert.Equal(t, m["Bob"], slice.Of(users[0])) - assert.Equal(t, m["Tom"], slice.Of(users[2])) - - g := loop.Group(loop.Of(users...), User.Name, as.Is) - assert.Equal(t, m, g) -} - -func Test_Valuer(t *testing.T) { - bob, bobRoles, _, _ := breakLoop.ExtraValue(breakLoop.From(loop.Of(users...)), User.Roles)() - - assert.Equal(t, bob, users[0]) - assert.Equal(t, bobRoles, users[0].roles) -} - -func Test_MultiValuer(t *testing.T) { - l := breakLoop.ExtraVals(breakLoop.From(loop.Of(users...)), User.Roles) - bob, bobRole, _, _ := l() - bob2, bobRole2, _, _ := l() - - assert.Equal(t, bob, users[0]) - assert.Equal(t, bob2, users[0]) - assert.Equal(t, bobRole, users[0].roles[0]) - assert.Equal(t, bobRole2, users[0].roles[1]) -} - -func Test_MultipleKeyValuer(t *testing.T) { - m, _ := breakKvLoop.Group(breakLoop.KeysValues(breakLoop.From(loop.Of(users...)), - func(u User) ([]string, error) { - return slice.Convert(u.roles, func(r Role) string { return strings.ToLower(r.name) }), nil - }, - func(u User) ([]string, error) { return []string{u.name, strings.ToLower(u.name)}, nil }, - )) - - assert.Equal(t, m["admin"], slice.Of("Bob", "bob")) - assert.Equal(t, m["manager"], slice.Of("Bob", "bob", "Alice", "alice")) - assert.Equal(t, m[""], slice.Of("Tom", "tom", "", "")) -} diff --git a/break/op/api.go b/break/op/api.go index 390d1d29..93ecb6e9 100644 --- a/break/op/api.go +++ b/break/op/api.go @@ -2,29 +2,28 @@ package op import ( - "golang.org/x/exp/constraints" + "cmp" - "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/op" ) // Sum returns the sum of two operands -func Sum[T c.Summable](a T, b T) (T, error) { +func Sum[T op.Summable](a T, b T) (T, error) { return op.Sum(a, b), nil } // Sub returns the substraction of the b from the a -func Sub[T c.Number](a T, b T) (T, error) { +func Sub[T op.Number](a T, b T) (T, error) { return op.Sub(a, b), nil } // Max returns the maximum from two operands -func Max[T constraints.Ordered](a T, b T) (T, error) { +func Max[T cmp.Ordered](a T, b T) (T, error) { return IfElse(a < b, b, a) } // Min returns the minimum from two operands -func Min[T constraints.Ordered](a T, b T) (T, error) { +func Min[T cmp.Ordered](a T, b T) (T, error) { return IfElse(a > b, b, a) } @@ -36,8 +35,8 @@ func IfElse[T any](ok bool, tru, fal T) (T, error) { return fal, nil } -// IfDoElse exececutes the tru func if ok, otherwise exec the fal function and returns it result -func IfDoElse[T any](ok bool, tru, fal func() (T, error)) (T, error) { +// IfGetElseGet executes the tru func if ok, otherwise exec the fal function and returns it result +func IfGetElseGet[T any](ok bool, tru, fal func() (T, error)) (T, error) { if ok { return tru() } diff --git a/break/op/test/api_test.go b/break/op/test/api_test.go index 83b972ae..da6c1808 100644 --- a/break/op/test/api_test.go +++ b/break/op/test/api_test.go @@ -5,32 +5,61 @@ import ( "github.com/stretchr/testify/assert" - "github.com/m4gshm/gollections/op" + "github.com/m4gshm/gollections/break/op" ) func Test_Min(t *testing.T) { - assert.Equal(t, 5, op.Min(5, 5)) - assert.Equal(t, 5, op.Min(5, 6)) - assert.Equal(t, "A", op.Min("a", "A")) + r, err := op.Min(5, 5) + assert.Equal(t, 5, r) + assert.NoError(t, err) + + r, err = op.Min(5, 6) + assert.Equal(t, 5, r) + assert.NoError(t, err) + + r2, err := op.Min("a", "A") + assert.Equal(t, "A", r2) + assert.NoError(t, err) } func Test_Max(t *testing.T) { - assert.Equal(t, 5, op.Max(5, 5)) - assert.Equal(t, 6, op.Max(5, 6)) - assert.Equal(t, "a", op.Max("a", "A")) + r, err := op.Max(5, 5) + assert.Equal(t, 5, r) + assert.NoError(t, err) + + r, err = op.Max(5, 6) + assert.Equal(t, 6, r) + assert.NoError(t, err) + + r2, err := op.Max("a", "A") + assert.Equal(t, "a", r2) + assert.NoError(t, err) } func Test_IfElse(t *testing.T) { - assert.Equal(t, 5, op.IfElse(true, 5, 6)) - assert.Equal(t, 6, op.IfElse(false, 5, 6)) + r, err := op.IfElse(true, 5, 6) + assert.Equal(t, 5, r) + assert.NoError(t, err) + + r, err = op.IfElse(false, 5, 6) + assert.Equal(t, 6, r) + assert.NoError(t, err) } func Test_IfElseDelay(t *testing.T) { - assert.Equal(t, 5, op.IfElse(true, func() int { return 5 }, func() int { return 6 })()) - assert.Equal(t, 6, op.IfElse(false, func() int { return 5 }, func() int { return 6 })()) + r, err := op.IfElse(true, func() int { return 5 }, func() int { return 6 }) + assert.Equal(t, 5, r()) + assert.NoError(t, err) + r, err = op.IfElse(false, func() int { return 5 }, func() int { return 6 }) + assert.Equal(t, 6, r()) + assert.NoError(t, err) } -func Test_IfDoElse(t *testing.T) { - assert.Equal(t, 5, op.IfGetElse(true, func() int { return 5 }, func() int { return 6 })) - assert.Equal(t, 6, op.IfGetElse(false, func() int { return 5 }, func() int { return 6 })) +func Test_IfGetElseGet(t *testing.T) { + r, err := op.IfGetElseGet(true, func() (int, error) { return 5, nil }, func() (int, error) { return 6, nil }) + assert.Equal(t, 5, r) + assert.NoError(t, err) + r, err = op.IfGetElseGet(false, func() (int, error) { return 5, nil }, func() (int, error) { return 6, nil }) + assert.Equal(t, 6, r) + assert.NoError(t, err) } diff --git a/break/predicate/api.go b/break/predicate/api.go index 3612ca3d..8b54eb2e 100644 --- a/break/predicate/api.go +++ b/break/predicate/api.go @@ -59,13 +59,15 @@ func Or[T any](p1, p2 Predicate[T]) Predicate[T] { // Xor makes an exclusive OR of two predicates func Xor[T any](p1, p2 Predicate[T]) Predicate[T] { return func(v T) (bool, error) { - if ok, err := p1(v); err != nil { + ok, err := p1(v) + if err != nil { return ok, err - } else if ok2, err := p2(v); err != nil { + } + ok2, err := p2(v) + if err != nil { return ok2, err - } else { - return ok != ok2, nil } + return ok != ok2, nil } } diff --git a/c/iface.go b/c/iface.go index ffb906c3..f78a2425 100644 --- a/c/iface.go +++ b/c/iface.go @@ -1,18 +1,6 @@ // Package c provides common types of containers, utility types and functions package c -import ( - "errors" - - "golang.org/x/exp/constraints" -) - -// Break is the 'break' statement of the For, Track methods -var Break = errors.New("Break") - -// Continue is an alias of the nil value used to continue iterating by For, Track methods. -var Continue error = nil - // Range provides an All function used for iterating over a sequence of elements by `for e := range collection.All`. type Range[T any] interface { All(yield func(T) bool) @@ -28,13 +16,6 @@ type KVRange[K, V any] interface { All(yield func(K, V) bool) } -// Iterable is a loop supplier interface -// -// Deprecated: obsolete. -type Iterable[T any, Loop ~func() (T, bool)] interface { - Loop() Loop -} - // KeyVal provides access to all keys and values of a key/value based collection. type KeyVal[K, V any] interface { Keys[K] @@ -53,25 +34,28 @@ type Values[V any] interface { // Collection is the base interface of non-associative collections type Collection[T any] interface { + Sized Range[T] - For[T] ForEach[T] SliceFactory[T] + Head() (T, bool) + First(func(T) bool) (T, bool) Reduce(merge func(T, T) T) T + HasAny(func(T) bool) bool } // Filterable provides filtering content functionality -type Filterable[T any, Loop ~func() (T, bool), LoopErr ~func() (T, bool, error)] interface { - Filter(predicate func(T) bool) Loop - Filt(predicate func(T) (bool, error)) LoopErr +type Filterable[T any, Seq ~func(yield func(T) bool), SeqE ~func(yield func(T, error) bool)] interface { + Filter(predicate func(T) bool) Seq + Filt(predicate func(T) (bool, error)) SeqE } // Convertable provides converaton of collection elements functionality -type Convertable[T any, Loop ~func() (T, bool), LoopErr ~func() (T, bool, error)] interface { - Convert(converter func(T) T) Loop - Conv(converter func(T) (T, error)) LoopErr +type Convertable[T any, Seq ~func(yield func(T) bool), SeqE ~func(yield func(T, error) bool)] interface { + Convert(converter func(T) T) Seq + Conv(converter func(T) (T, error)) SeqE } // SliceFactory collects the elements of the collection into a slice @@ -92,7 +76,6 @@ type Iterator[T any] interface { // If ok == false, then the iteration must be completed. Next() (out T, ok bool) - For[T] ForEach[T] Range[T] } @@ -100,49 +83,16 @@ type Iterator[T any] interface { // Sized - storage interface with measurable size type Sized interface { // returns an estimated internal storage size or -1 if the size cannot be calculated - Size() int -} - -// PrevIterator is the Iterator that provides reverse iteration over elements of a collection -type PrevIterator[T any] interface { - Iterator[T] - //retrieves a prev element and true or zero value of T and false if no more elements - Prev() (T, bool) -} - -// DelIterator is the Iterator provides deleting of current element. -type DelIterator[T any] interface { - Iterator[T] - Delete() -} - -// For is the interface of a collection that provides traversing of the elements. -// -// Deprecated: obsolete. -type For[IT any] interface { - //For takes elements of the collection. Can be interrupt by returning Break. - For(func(element IT) error) error + Len() int } // ForEach is the interface of a collection that provides traversing of the elements without error checking. -// -// Deprecated: obsolete. type ForEach[T any] interface { // ForEach takes all elements of the collection ForEach(func(element T)) } -// Track is the interface of a collection that provides traversing of the elements with position tracking (index, key, coordinates, etc.). -// -// Deprecated: obsolete. -type Track[P any, T any] interface { - // return Break for loop breaking - Track(func(position P, element T) error) error -} - // TrackEach is the interface of a collection that provides traversing of the elements with position tracking (index, key, coordinates, etc.) without error checking. -// -// Deprecated: obsolete. type TrackEach[P any, T any] interface { TrackEach(func(position P, element T)) } @@ -219,13 +169,3 @@ type ImmutableMapConvert[M any] interface { type Removable[P any, V any] interface { Remove(P) (V, bool) } - -// Summable is a type that supports the operator + -type Summable interface { - constraints.Ordered | constraints.Complex | string -} - -// Number is a type that supports the operators +, -, /, * -type Number interface { - constraints.Integer | constraints.Float | constraints.Complex -} diff --git a/collection/api.go b/collection/api.go index 72f7e078..8239b5a1 100644 --- a/collection/api.go +++ b/collection/api.go @@ -2,128 +2,112 @@ package collection import ( - "golang.org/x/exp/constraints" + "cmp" - breakLoop "github.com/m4gshm/gollections/break/loop" + "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/comparer" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" - loopconvert "github.com/m4gshm/gollections/loop/convert" "github.com/m4gshm/gollections/op/check/not" + "github.com/m4gshm/gollections/seq" ) -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To any, IT Iterable[From]](collection IT, converter func(From) To) loop.Loop[To] { - b := collection.Loop() - return loop.Convert(b, converter) +// Head returns the first element. +func Head[IT c.Range[T], T any](collection IT) (T, bool) { + return seq.Head(collection.All) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To any, IT Iterable[From]](collection IT, converter func(From) (To, error)) breakLoop.Loop[To] { - b := collection.Loop() - return loop.Conv(b, converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[IT c.Range[From], From, To any](collection IT, converter func(From) To) seq.Seq[To] { + return seq.Convert(collection.All, converter) } -// FilterAndConvert returns a loop that filters source elements and converts them -func FilterAndConvert[From, To any, IT Iterable[From]](collection IT, filter func(From) bool, converter func(From) To) loop.Loop[To] { - b := collection.Loop() - f := loop.FilterAndConvert(b, filter, converter) - return f +// ConvertNilSafe creates a seq that filters not nil elements, converts that ones, filters not nils after converting and returns them +func ConvertNilSafe[IT c.Range[*From], From, To any](collection IT, converter func(*From) *To) seq.Seq[*To] { + h := collection.All + return seq.ConvertNilSafe(h, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To any, IT Iterable[From]](collection IT, by func(From) []To) loop.Loop[To] { - b := collection.Loop() - f := loop.Flat(b, by) - return f +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[IT c.Range[From], From, To any](collection IT, converter func(From) (To, error)) seq.SeqE[To] { + return seq.Conv(collection.All, converter) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable, IT Iterable[From]](collection IT, flattener func(From) ([]To, error)) breakLoop.Loop[To] { - return loop.Flatt(collection.Loop(), flattener) +// FilterAndConvert returns a seq that filters source elements and converts them +func FilterAndConvert[IT c.Range[From], From, To any](collection IT, filter func(From) bool, converter func(From) To) seq.Seq[To] { + return seq.Convert(seq.Filter(collection.All, filter), converter) } -// FilterAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function -func FilterAndFlat[From, To any, IT Iterable[From]](collection IT, filter func(From) bool, flattener func(From) []To) loop.Loop[To] { - b := collection.Loop() - f := loop.FilterAndFlat(b, filter, flattener) - return f +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[IT c.Range[From], From, To any](collection IT, by func(From) []To) seq.Seq[To] { + return seq.Flat(collection.All, by) } -// Filter instantiates a loop that checks elements by the 'filter' function and returns successful ones -func Filter[T any, IT Iterable[T]](collection IT, filter func(T) bool) loop.Loop[T] { - b := collection.Loop() - f := loop.Filter(b, filter) - return f +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[IT c.Range[From], From, To any](collection IT, flattener func(From) ([]To, error)) seq.SeqE[To] { + return seq.Flatt(collection.All, flattener) } -// NotNil instantiates a loop that filters nullable elements -func NotNil[T any, IT Iterable[*T]](collection IT) loop.Loop[*T] { - return Filter(collection, not.Nil[T]) +// FilterAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function +func FilterAndFlat[IT c.Range[From], From, To any](collection IT, filter func(From) bool, flattener func(From) []To) seq.Seq[To] { + return seq.Flat(seq.Filter(collection.All, filter), flattener) } -// PtrVal creates a loop that transform pointers to the values referenced referenced by those pointers. -// Nil pointers are transformet to zero values. -func PtrVal[T any, IT Iterable[*T]](collection IT) loop.Loop[T] { - return loop.PtrVal(collection.Loop()) +// Filter instantiates a seq that checks elements by the 'filter' function and returns successful ones. +func Filter[IT c.Range[T], T any](collection IT, filter func(T) bool) seq.Seq[T] { + return seq.Filter(collection.All, filter) } -// NoNilPtrVal creates a loop that transform only not nil pointers to the values referenced referenced by those pointers. -// Nil pointers are ignored. -func NoNilPtrVal[T any, IT Iterable[*T]](collection IT) loop.Loop[T] { - return loop.NoNilPtrVal(collection.Loop()) +// Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. +func Filt[IT c.Range[T], T any](collection IT, filter func(T) (bool, error)) seq.SeqE[T] { + return seq.Filt(collection.All, filter) } -// NilSafe creates a loop that filters not nil elements, converts that ones, filters not nils after converting and returns them -func NilSafe[From, To any, IT Iterable[*From]](collection IT, converter func(*From) *To) loop.Loop[*To] { - h := collection.Loop() - return loopconvert.NilSafe(h, converter) +// NotNil instantiates a seq that filters nullable elements +func NotNil[IT c.Range[*T], T any](collection IT) seq.Seq[*T] { + return Filter(collection, not.Nil[T]) } // KeyValue transforms iterable elements to key/value iterator based on applying key, value extractors to the elements -func KeyValue[T any, K comparable, V any, IT Iterable[T]](collection IT, keyExtractor func(T) K, valExtractor func(T) V) kvloop.Loop[K, V] { - h := collection.Loop() - return loop.KeyValue(h, keyExtractor, valExtractor) +func KeyValue[IT c.Range[T], T any, K comparable, V any](collection IT, keyExtractor func(T) K, valExtractor func(T) V) seq.Seq2[K, V] { + return seq.ToKV(collection.All, keyExtractor, valExtractor) } -// First returns the first element that satisfies the condition of the 'predicate' function -func First[T any, IT Iterable[T]](collection IT, predicate func(T) bool) (v T, ok bool) { - i := collection.Loop() - return loop.First(i, predicate) +// First returns the first element that satisfies the condition. +func First[IT c.Range[T], T any](collection IT, condition func(T) bool) (v T, ok bool) { + return seq.First(collection.All, condition) } -// Firstt returns the first element that satisfies the condition of the 'predicate' function -func Firstt[T any, IT Iterable[T]](collection IT, predicate func(T) (bool, error)) (v T, ok bool, err error) { - return loop.Firstt(collection.Loop(), predicate) +// Firstt returns the first element that satisfies the condition. +func Firstt[IT c.Range[T], T any](collection IT, condition func(T) (bool, error)) (v T, ok bool, err error) { + return seq.Firstt(collection.All, condition) } // Sort sorts the specified sortable collection that contains orderable elements func Sort[SC any, Cmp ~func(T, T) int, C interface { Sort(Cmp) SC -}, T any, O constraints.Ordered](collection C, order func(T) O) SC { +}, T any, O cmp.Ordered](collection C, order func(T) O) SC { return collection.Sort(comparer.Of(order)) } // Reduce reduces the 'collection' elements into an one using the 'merge' function. // If the 'collection' is empty, the zero value of 'T' type is returned. -func Reduce[T any, IT Iterable[T]](collection IT, merge func(T, T) T) T { - return loop.Reduce(collection.Loop(), merge) +func Reduce[IT c.Range[T], T any](collection IT, merge func(T, T) T) T { + return seq.Reduce(collection.All, merge) } // Reducee reduces the 'collection' elements into an one using the 'merge' function. // Returns ok==false if the 'collection' is empty. -func Reducee[T any, IT Iterable[T]](collection IT, merge func(T, T) (T, error)) (T, error) { - return loop.Reducee(collection.Loop(), merge) +func Reducee[IT c.Range[T], T any](collection IT, merge func(T, T) (T, error)) (T, error) { + return seq.Reducee(collection.All, merge) } // 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 of the 'collection'. -func Accum[T any, IT Iterable[T]](first T, collection IT, merge func(T, T) T) T { - return loop.Accum(first, collection.Loop(), merge) +func Accum[IT c.Range[T], T any](first T, collection IT, merge func(T, T) T) T { + return seq.Accum(first, collection.All, merge) } // 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 of the 'collection'. -func Accumm[T any, IT Iterable[T]](first T, collection IT, merge func(T, T) (T, error)) (T, error) { - return loop.Accumm(first, collection.Loop(), merge) +func Accumm[IT c.Range[T], T any](first T, collection IT, merge func(T, T) (T, error)) (T, error) { + return seq.Accumm(first, collection.All, merge) } // IsEmpty returns true if the collection is empty diff --git a/collection/convert/api.go b/collection/convert/api.go deleted file mode 100644 index f2117050..00000000 --- a/collection/convert/api.go +++ /dev/null @@ -1,24 +0,0 @@ -// Package convert provides converation helpers for collection implementations -package convert - -import ( - "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op/check/not" -) - -// AndConvert - convert.AndConvert makes double converts From->Intermediate->To of the elements -func AndConvert[From, To, Too any, IT collection.Iterable[From]](elements IT, firsConverter func(From) To, secondConverter func(To) Too) loop.Loop[Too] { - cc := loop.Convert(collection.Convert(elements, firsConverter), secondConverter) - return cc -} - -// AndFilter - convert.AndFilter converts only filtered elements and returns them -func AndFilter[From, To any, IT collection.Iterable[From]](elements IT, converter func(From) To, filter func(To) bool) loop.Loop[To] { - return loop.Filter(collection.Convert(elements, converter), filter) -} - -// NotNil - convert.NotNil converts only not nil elements and returns them -func NotNil[From, To any, IT collection.Iterable[*From]](elements IT, converter func(*From) To) loop.Loop[To] { - return collection.FilterAndConvert(elements, not.Nil[From], converter) -} diff --git a/collection/filter/api.go b/collection/filter/api.go deleted file mode 100644 index 0e4401e9..00000000 --- a/collection/filter/api.go +++ /dev/null @@ -1,17 +0,0 @@ -// Package filter provides aliases for collections filtering helpers -package filter - -import ( - "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" -) - -// AndConvert - filter.AndConvert is short alias of collection.FilterAndConvert -func AndConvert[From, To any, IT collection.Iterable[From]](elements IT, filter func(From) bool, converter func(From) To) loop.Loop[To] { - return collection.FilterAndConvert(elements, filter, converter) -} - -// ConvertFilter - filter.ConvertFilter is short alias of slice.FilterConvertFilter -func ConvertFilter[From, To any, IT collection.Iterable[From]](elements IT, filterFrom func(From) bool, converter func(From) To, filterTo func(To) bool) loop.Loop[To] { - return collection.FilterAndConvert(elements, filterFrom, converter).Filter(filterTo) -} diff --git a/collection/iface.go b/collection/iface.go index 844c3781..44aa1c03 100644 --- a/collection/iface.go +++ b/collection/iface.go @@ -1,37 +1,25 @@ package collection import ( - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" kv "github.com/m4gshm/gollections/kv/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/seq" ) -// Iterable is a loop supplier interface -// -// Deprecated: obsolete. -type Iterable[T any] c.Iterable[T, loop.Loop[T]] - // Collection is the base interface for the Vector and the Set impelementations type Collection[T any] interface { - Iterable[T] c.Collection[T] - c.Filterable[T, loop.Loop[T], breakLoop.Loop[T]] - c.Convertable[T, loop.Loop[T], breakLoop.Loop[T]] + c.Filterable[T, seq.Seq[T], seq.SeqE[T]] + c.Convertable[T, seq.Seq[T], seq.SeqE[T]] Len() int IsEmpty() bool - - HasAny(func(T) bool) bool } // Vector - collection interface that provides elements order and access by index to the elements. type Vector[T any] interface { Collection[T] - - c.Track[int, T] c.TrackEach[int, T] - c.Access[int, T] } @@ -44,8 +32,8 @@ type Set[T comparable] interface { // Map - collection interface that stores key/value pairs and provide access to an element by its key type Map[K comparable, V any] interface { kv.Collection[K, V, map[K]V] - kv.Filterable[K, V] - kv.Convertable[K, V] + kv.Filterable[K, V, seq.Seq2[K, V], seq.SeqE[c.KV[K, V]]] + kv.Convertable[K, V, seq.Seq2[K, V], seq.SeqE[c.KV[K, V]]] c.Checkable[K] c.Access[K, V] c.KVRange[K, V] diff --git a/collection/immutable/api.go b/collection/immutable/api.go index 0ac70471..82189644 100644 --- a/collection/immutable/api.go +++ b/collection/immutable/api.go @@ -4,8 +4,6 @@ package immutable import ( "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection/immutable/ordered" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/seq2" @@ -19,15 +17,7 @@ func NewSet[T comparable](elements ...T) Set[T] { // NewSetOrdered instantiates ordered set and copies elements to it func NewSetOrdered[T comparable](elements ...T) ordered.Set[T] { - return ordered.NewSet[T](elements...) -} - -// SetFromLoop creates a set with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [SetFromSeq]. -func SetFromLoop[T comparable](next func() (T, bool)) Set[T] { - return SetFromSeq((loop.Loop[T])(next).All) + return ordered.NewSet(elements...) } // SetFromSeq creates a set with elements retrieved by the seq. @@ -57,13 +47,6 @@ func NewMapOf[K comparable, V any](elements map[K]V) Map[K, V] { return MapFromSeq2(seq2.OfMap(elements)) } -// MapFromLoop creates a map with elements retrieved converter the 'next' function. -// -// Deprecated: replaced by [MapFromSeq2]. -func MapFromLoop[K comparable, V any](next func() (K, V, bool)) Map[K, V] { - return MapFromSeq2(kvloop.Loop[K, V](next).All) -} - // MapFromSeq2 creates a map with elements retrieved by the seq. func MapFromSeq2[K comparable, V any](seq seq.Seq2[K, V]) Map[K, V] { uniques := map[K]V{} @@ -78,14 +61,6 @@ func NewVector[T any](elements ...T) Vector[T] { return WrapVector(slice.Clone(elements)) } -// VectorFromLoop creates a vector with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [VectorFromLoop]. -func VectorFromLoop[T any](next func() (T, bool)) Vector[T] { - return WrapVector(loop.Slice(next)) -} - // VectorFromSeq creates a vector with elements retrieved by the seq. func VectorFromSeq[T any](s seq.Seq[T]) Vector[T] { return WrapVector(seq.Slice(s)) diff --git a/collection/immutable/keys.go b/collection/immutable/keys.go index 5f9e2d39..b1d2a624 100644 --- a/collection/immutable/keys.go +++ b/collection/immutable/keys.go @@ -3,10 +3,10 @@ package immutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -32,31 +32,9 @@ func (m MapKeys[K, V]) All(consumer func(K) bool) { map_.TrackKeysWhile(m.elements, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K, V]) Loop() loop.Loop[K] { - h := m.Head() - return (&h).Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K, V]) Head() map_.KeyIter[K, V] { - return map_.NewKeyIter(m.elements) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K, V]) First() (map_.KeyIter[K, V], K, bool) { - var ( - iterator = m.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok +// Head returns the first element. +func (m MapKeys[K, V]) Head() (K, bool) { + return collection.Head(m) } // Len returns amount of elements @@ -79,34 +57,29 @@ func (m MapKeys[K, V]) Append(out []K) []K { return map_.AppendKeys(m.elements, out) } -// For applies the 'consumer' function for every key until the consumer returns the c.Break to stop. -func (m MapKeys[K, V]) For(consumer func(K) error) error { - return map_.ForKeys(m.elements, consumer) -} - // ForEach applies the 'consumer' function for every key func (m MapKeys[K, V]) ForEach(consumer func(K)) { map_.ForEachKey(m.elements, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapKeys[K, V]) Filter(filter func(K) bool) loop.Loop[K] { - return loop.Filter(m.Loop(), filter) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapKeys[K, V]) Filter(filter func(K) bool) seq.Seq[K] { + return collection.Filter(m, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapKeys[K, V]) Filt(predicate func(K) (bool, error)) breakLoop.Loop[K] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapKeys[K, V]) Filt(filter func(K) (bool, error)) seq.SeqE[K] { + return collection.Filt(m, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m MapKeys[K, V]) Convert(converter func(K) K) loop.Loop[K] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m MapKeys[K, V]) Convert(converter func(K) K) seq.Seq[K] { + return collection.Convert(m, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m MapKeys[K, V]) Conv(converter func(K) (K, error)) breakLoop.Loop[K] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m MapKeys[K, V]) Conv(converter func(K) (K, error)) seq.SeqE[K] { + return collection.Conv(m, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -117,11 +90,15 @@ func (m MapKeys[K, V]) Reduce(merge func(K, K) K) K { return k } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (m MapKeys[K, V]) HasAny(predicate func(K) bool) bool { - return map_.HasAny(m.elements, func(k K, _ V) bool { - return predicate(k) - }) +// HasAny checks whether the collection contains a key that satisfies the condition. +func (m MapKeys[K, V]) HasAny(condition func(K) bool) bool { + return map_.HasAny(m.elements, predicate.Key[V](condition)) +} + +// First returns the first key\value pair that satisfies the condition. +func (m MapKeys[K, V]) First(condition func(K) bool) (K, bool) { + k, _, ok := map_.First(m.elements, predicate.Key[V](condition)) + return k, ok } func (m MapKeys[K, V]) String() string { diff --git a/collection/immutable/map.go b/collection/immutable/map.go index 6b0c5fa7..1f2ddd1a 100644 --- a/collection/immutable/map.go +++ b/collection/immutable/map.go @@ -3,16 +3,16 @@ package immutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/kv/loop" - breakMapFilter "github.com/m4gshm/gollections/break/kv/predicate" - breakMapConvert "github.com/m4gshm/gollections/break/map_/convert" + converte "github.com/m4gshm/gollections/break/kv/convert" + kvFiltere "github.com/m4gshm/gollections/break/kv/predicate" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable/ordered" "github.com/m4gshm/gollections/kv/convert" - "github.com/m4gshm/gollections/kv/loop" - filter "github.com/m4gshm/gollections/kv/predicate" + kvFilter "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" + "github.com/m4gshm/gollections/seq2" "github.com/m4gshm/gollections/slice" ) @@ -44,31 +44,9 @@ func (m Map[K, V]) All(consumer func(k K, v V) bool) { } } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) Loop() loop.Loop[K, V] { - h := m.Head() - return h.Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) Head() map_.Iter[K, V] { - return map_.NewIter(m.elements) -} - -// First returns the first key/value pair of the map, an iterator to iterate over the remaining pair, and true\false marker of availability next pairs. -// If no more then ok==false. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) First() (map_.Iter[K, V], K, V, bool) { - var ( - iterator = m.Head() - firstK, firstV, ok = iterator.Next() - ) - return iterator, firstK, firstV, ok +// Head returns the first key\value pair. +func (m Map[K, V]) Head() (K, V, bool) { + return seq2.Head(m.All) } // Map collects the key/value pairs into a new map @@ -131,74 +109,69 @@ func (m Map[K, V]) String() string { return map_.ToString(m.elements) } -// Track applies the 'consumer' function for all key/value pairs until the consumer returns the c.Break to stop. -func (m Map[K, V]) Track(consumer func(K, V) error) error { - return map_.Track(m.elements, consumer) -} - // TrackEach applies the 'consumer' function for every key/value pairs func (m Map[K, V]) TrackEach(consumer func(K, V)) { map_.TrackEach(m.elements, consumer) } -// FilterKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FilterKey(predicate func(K) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Key[V](predicate)) +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterKey(filter func(K) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvFilter.Key[V](filter)) } -// FiltKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltKey(predicate func(K) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Key[V](predicate)) +// FiltKey returns an errorable seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltKey(filter func(K) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, kvFiltere.Key[V](filter)) } -// ConvertKey returns a loop that applies the 'converter' function to keys of the map -func (m Map[K, V]) ConvertKey(by func(K) K) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Key[V](by)) +// ConvertKey returns a seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvertKey(converter func(K) K) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Key[V](converter)) } -// ConvKey returns a loop that applies the 'converter' function to keys of the map -func (m Map[K, V]) ConvKey(converter func(K) (K, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Key[V](converter)) +// ConvKey returns an errorable seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvKey(converter func(K) (K, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Key[V](converter)) } -// FilterValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m Map[K, V]) FilterValue(predicate func(V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Value[K](predicate)) +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterValue(filter func(V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvFilter.Value[K](filter)) } -// FiltValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltValue(predicate func(V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Value[K](predicate)) +// FiltValue returns an errorable seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltValue(filter func(V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, kvFiltere.Value[K](filter)) } -// ConvertValue returns a loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvertValue(by func(V) V) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Value[K](by)) +// ConvertValue returns a seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvertValue(converter func(V) V) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Value[K](converter)) } -// ConvValue returns a loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvValue(converter func(V) (V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Value[K](converter)) +// ConvValue returns an errorable seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvValue(converter func(V) (V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Value[K](converter)) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m Map[K, V]) Filter(predicate func(K, V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filter(filter func(K, V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m Map[K, V]) Filt(predicate func(K, V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filt(filter func(K, V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m Map[K, V]) Convert(converter func(K, V) (K, V)) loop.Loop[K, V] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Convert(converter func(K, V) (K, V)) seq.Seq2[K, V] { + return seq2.Convert(m.All, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m Map[K, V]) Conv(converter func(K, V) (K, V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Conv(converter func(K, V) (K, V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converter) } // Reduce reduces the key/value pairs of the map into an one pair using the 'merge' function @@ -206,7 +179,7 @@ func (m Map[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (K, V) { return map_.Reduce(m.elements, merge) } -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func (m Map[K, V]) HasAny(predicate func(K, V) bool) bool { - return map_.HasAny(m.elements, predicate) +// HasAny checks whether the map contains a key/value pair that satisfies the condition. +func (m Map[K, V]) HasAny(condition func(K, V) bool) bool { + return map_.HasAny(m.elements, condition) } diff --git a/collection/immutable/map_/api.go b/collection/immutable/map_/api.go index 3f5c0729..c84b560b 100644 --- a/collection/immutable/map_/api.go +++ b/collection/immutable/map_/api.go @@ -17,14 +17,6 @@ func New[K comparable, V any](elements map[K]V) immutable.Map[K, V] { return immutable.NewMapOf(elements) } -// From instantiates a map with key/values retrieved by the 'next' function. -// The next returns a key/value pairs with true or zero values with false if there are no more elements. -// -// Deprecated: replaced by [FromSeq2]. -func From[K comparable, V any](next func() (K, V, bool)) immutable.Map[K, V] { - return immutable.MapFromLoop(next) -} - // FromSeq2 creates a map with elements retrieved by the seq. func FromSeq2[K comparable, V any](seq seq.Seq2[K, V]) immutable.Map[K, V] { return immutable.MapFromSeq2(seq) diff --git a/collection/immutable/map_/test/map_test.go b/collection/immutable/map_/test/map_test.go index f1626b32..da673063 100644 --- a/collection/immutable/map_/test/map_test.go +++ b/collection/immutable/map_/test/map_test.go @@ -11,7 +11,6 @@ import ( "github.com/m4gshm/gollections/collection/immutable/map_" "github.com/m4gshm/gollections/collection/immutable/ordered" "github.com/m4gshm/gollections/k" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -22,13 +21,8 @@ func Test_Map_Of(t *testing.T) { iterCheck(t, m) } -func Test_Map_From(t *testing.T) { - m := map_.From(loop.KeyValue(loop.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) - iterCheck(t, m) -} - func Test_Map_FromSeq(t *testing.T) { - m := map_.FromSeq2(seq.KeyValue(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) + m := map_.FromSeq2(seq.ToKV(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) iterCheck(t, m) } @@ -67,7 +61,7 @@ func Test_Map_Iterate_Keys(t *testing.T) { expectedK := slice.Of(1, 2, 3, 4) keys := []int{} - for it, key, ok := dict.Keys().First(); ok; key, ok = it.Next() { + for key := range dict.Keys().All { keys = append(keys, key) } @@ -83,7 +77,7 @@ func Test_Map_Iterate_Values(t *testing.T) { expectedV := slice.Of("1", "2", "3", "4") values := []string{} - for it, val, ok := ordered.Values().First(); ok; val, ok = it.Next() { + for val := range ordered.Values().All { values = append(values, val) } @@ -102,42 +96,31 @@ func Test_Map_Zero(t *testing.T) { e := m.IsEmpty() assert.True(t, e) - head, _, _, ok := m.First() - assert.False(t, ok) - _, _, ok = head.Next() - assert.False(t, ok) - - head = m.Head() - _, _, ok = head.Next() + _, _, ok := m.Head() assert.False(t, ok) _, ok = m.Get("") assert.False(t, ok) - m.Track(nil) m.TrackEach(nil) m.Filter(nil) m.FilterKey(nil) m.FilterValue(nil) - m.Values().For(nil) m.Values().ForEach(nil) - m.ConvertValue(nil).Track(nil) + m.ConvertValue(nil).TrackEach(nil) m.ConvertValue(nil).Filter(nil).FilterKey(nil) m.ConvertValue(nil).Filter(nil).FilterValue(nil) - m.Keys().For(nil) m.Keys().ForEach(nil) - m.ConvertKey(nil).Track(nil) + m.ConvertKey(nil).TrackEach(nil) m.ConvertKey(nil).Filter(nil).FilterKey(nil) m.ConvertKey(nil).Filter(nil).FilterValue(nil) m.Convert(nil) - m.Sort(nil).Track(nil) m.Sort(nil).TrackEach(nil) - m.StableSort(nil).Track(nil) m.StableSort(nil).TrackEach(nil) } diff --git a/collection/immutable/ordered/api.go b/collection/immutable/ordered/api.go index 9bdf9377..50b94022 100644 --- a/collection/immutable/ordered/api.go +++ b/collection/immutable/ordered/api.go @@ -3,8 +3,6 @@ package ordered import ( "github.com/m4gshm/gollections/c" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice/clone" ) @@ -14,14 +12,6 @@ func NewSet[T comparable](elements ...T) Set[T] { return SetFromSeq(seq.Of(elements...)) } -// SetFromLoop creates a set with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [SetFromSeq]. -func SetFromLoop[T comparable](next func() (T, bool)) Set[T] { - return SetFromSeq(loop.Loop[T](next).All) -} - // SetFromSeq creates a set with elements retrieved by the seq. func SetFromSeq[T comparable](seq seq.Seq[T]) Set[T] { if seq == nil { @@ -60,13 +50,6 @@ func NewMapOf[K comparable, V any](order []K, elements map[K]V) Map[K, V] { return WrapMap(clone.Of(order), uniques) } -// MapFromLoop creates a map with elements retrieved converter the 'next' function. -// -// Deprecated: replaced by [MapFromSeq2]. -func MapFromLoop[K comparable, V any](next func() (K, V, bool)) Map[K, V] { - return MapFromSeq2(kvloop.Loop[K, V](next).All) -} - // MapFromSeq2 creates a map with elements retrieved by the seq. func MapFromSeq2[K comparable, V any](seq seq.Seq2[K, V]) Map[K, V] { if seq == nil { diff --git a/collection/immutable/ordered/keys.go b/collection/immutable/ordered/keys.go index 29308d61..e682a269 100644 --- a/collection/immutable/ordered/keys.go +++ b/collection/immutable/ordered/keys.go @@ -3,10 +3,9 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -38,27 +37,9 @@ func (m MapKeys[K]) IAll(consumer func(int, K) bool) { slice.TrackWhile(m.keys, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K]) Loop() loop.Loop[K] { - return loop.Of(m.keys...) -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K]) Head() slice.Iter[K] { - return slice.NewHead(m.keys) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [MapKeys.All]. -func (m MapKeys[K]) First() (*slice.Iter[K], K, bool) { - h := m.Head() - return h.Crank() +// Head returns the first element. +func (m MapKeys[K]) Head() (K, bool) { + return collection.Head(m) } // Len returns amount of elements @@ -87,34 +68,29 @@ func (m MapKeys[K]) Append(out []K) []K { return out } -// For applies the 'consumer' function for every key until the consumer returns the c.Break to stop. -func (m MapKeys[K]) For(consumer func(K) error) error { - return slice.For(m.keys, consumer) -} - // ForEach applies the 'consumer' function for every element func (m MapKeys[K]) ForEach(consumer func(K)) { slice.ForEach(m.keys, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapKeys[K]) Filter(filter func(K) bool) loop.Loop[K] { - return loop.Filter(m.Loop(), filter) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapKeys[K]) Filter(filter func(K) bool) seq.Seq[K] { + return collection.Filter(m, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapKeys[K]) Filt(predicate func(K) (bool, error)) breakLoop.Loop[K] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapKeys[K]) Filt(filter func(K) (bool, error)) seq.SeqE[K] { + return collection.Filt(m, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m MapKeys[K]) Convert(converter func(K) K) loop.Loop[K] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m MapKeys[K]) Convert(converter func(K) K) seq.Seq[K] { + return collection.Convert(m, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m MapKeys[K]) Conv(converter func(K) (K, error)) breakLoop.Loop[K] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m MapKeys[K]) Conv(converter func(K) (K, error)) seq.SeqE[K] { + return collection.Conv(m, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -122,9 +98,14 @@ func (m MapKeys[K]) Reduce(merge func(K, K) K) K { return slice.Reduce(m.keys, merge) } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (m MapKeys[K]) HasAny(predicate func(K) bool) bool { - return slice.HasAny(m.keys, predicate) +// HasAny checks whether the collection contains a key that satisfies the condition. +func (m MapKeys[K]) HasAny(condition func(K) bool) bool { + return slice.HasAny(m.keys, condition) +} + +// First returns the first key that satisfies requirements of the condition +func (m MapKeys[K]) First(condition func(K) bool) (K, bool) { + return slice.First(m.keys, condition) } // String returns string representation of the collection diff --git a/collection/immutable/ordered/map.go b/collection/immutable/ordered/map.go index 1427d854..722e36e0 100644 --- a/collection/immutable/ordered/map.go +++ b/collection/immutable/ordered/map.go @@ -3,19 +3,19 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/kv/loop" - breakMapFilter "github.com/m4gshm/gollections/break/kv/predicate" - breakMapConvert "github.com/m4gshm/gollections/break/map_/convert" + converte "github.com/m4gshm/gollections/break/kv/convert" + filtere "github.com/m4gshm/gollections/break/kv/predicate" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/kv/convert" - kvloop "github.com/m4gshm/gollections/kv/loop" - filter "github.com/m4gshm/gollections/kv/predicate" + kvfilter "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" + "github.com/m4gshm/gollections/seq2" "github.com/m4gshm/gollections/slice" ) -// WrapMap instantiates ordered Map using a map and an order slice as internal storage. +// WrapMap instantiates an ordered Map using a map and an order slice as internal storage. func WrapMap[K comparable, V any](order []K, elements map[K]V) Map[K, V] { return Map[K, V]{order: order, elements: elements} } @@ -40,38 +40,9 @@ func (m Map[K, V]) All(consumer func(K, V) bool) { map_.TrackOrderedWhile(m.order, m.elements, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) Loop() kvloop.Loop[K, V] { - h := m.Head() - return h.Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) Head() MapIter[K, V] { - return NewMapIter(m.elements, slice.NewHead(m.order)) -} - -// First returns the first key/value pair of the map, an iterator to iterate over the remaining pair, and true\false marker of availability next pairs. -// If no more then ok==false. -// -// Deprecated: replaced by [Map.All]. -func (m Map[K, V]) First() (MapIter[K, V], K, V, bool) { - var ( - iterator = m.Head() - firstK, firstV, ok = iterator.Next() - ) - return iterator, firstK, firstV, ok -} - -// Tail creates an iterator pointing to the end of the map -// -// Deprecated: Tail is deprecated. Will be replaced by a rance-over function iterator. -func (m Map[K, V]) Tail() MapIter[K, V] { - return NewMapIter(m.elements, slice.NewTail(m.order)) +// Head returns the first key\value pair. +func (m Map[K, V]) Head() (K, V, bool) { + return seq2.Head(m.All) } // Map collects the key/value pairs into a new map @@ -134,86 +105,69 @@ func (m Map[K, V]) String() string { return map_.ToStringOrdered(m.order, m.elements) } -// Track applies the 'consumer' function for all key/value pairs until the consumer returns the c.Break to stop. -func (m Map[K, V]) Track(consumer func(K, V) error) error { - return map_.TrackOrdered(m.order, m.elements, consumer) -} - // TrackEach applies the 'consumer' function for every key/value pairs func (m Map[K, V]) TrackEach(consumer func(K, V)) { map_.TrackEachOrdered(m.order, m.elements, consumer) } -// FilterKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FilterKey(predicate func(K) bool) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Filter(h.Next, filter.Key[V](predicate)) +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterKey(filter func(K) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Key[V](filter)) } -// FiltKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltKey(predicate func(K) (bool, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Filt(h.Next, breakMapFilter.Key[V](predicate)) +// FiltKey returns an errorable seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltKey(filter func(K) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Key[V](filter)) } -// ConvertKey returns a loop that applies the 'converter' function to keys of the map -func (m Map[K, V]) ConvertKey(by func(K) K) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Convert(h.Next, convert.Key[V](by)) +// ConvertKey returns a seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvertKey(converter func(K) K) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Key[V](converter)) } -// ConvKey returns a loop that applies the 'converter' function to keys of the map -func (m Map[K, V]) ConvKey(converter func(K) (K, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Conv(h.Next, breakMapConvert.Key[V](converter)) +// ConvKey returns an errorable seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvKey(converter func(K) (K, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Key[V](converter)) } -// FilterValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m Map[K, V]) FilterValue(predicate func(V) bool) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Filter(h.Next, filter.Value[K](predicate)) +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterValue(filter func(V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Value[K](filter)) } -// FiltValue returns a breakable loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltValue(predicate func(V) (bool, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Filt(h.Next, breakMapFilter.Value[K](predicate)) +// FiltValue returns an errorable seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltValue(filter func(V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Value[K](filter)) } -// ConvertValue returns a loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvertValue(converter func(V) V) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Convert(h.Next, convert.Value[K](converter)) +// ConvertValue returns a seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvertValue(converter func(V) V) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Value[K](converter)) } -// ConvValue returns a breakable loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvValue(converter func(V) (V, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Conv(h.Next, breakMapConvert.Value[K](converter)) +// ConvValue returns an errorable seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvValue(converter func(V) (V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Value[K](converter)) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m Map[K, V]) Filter(predicate func(K, V) bool) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Filter(h.Next, predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filter(filter func(K, V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m Map[K, V]) Filt(predicate func(K, V) (bool, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Filt(h.Next, predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filt(filter func(K, V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m Map[K, V]) Convert(converter func(K, V) (K, V)) kvloop.Loop[K, V] { - h := m.Head() - return kvloop.Convert(h.Next, converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Convert(converter func(K, V) (K, V)) seq.Seq2[K, V] { + return seq2.Convert(m.All, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m Map[K, V]) Conv(converter func(K, V) (K, V, error)) breakLoop.Loop[K, V] { - h := m.Head() - return kvloop.Conv(h.Next, converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Conv(converter func(K, V) (K, V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converter) } // Reduce reduces the key/value pairs of the map into an one pair using the 'merge' function @@ -221,9 +175,9 @@ func (m Map[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (K, V) { return map_.Reduce(m.elements, merge) } -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func (m Map[K, V]) HasAny(predicate func(K, V) bool) bool { - return map_.HasAny(m.elements, predicate) +// HasAny checks whether the map contains a key/va;ue pair that satisfies the condition. +func (m Map[K, V]) HasAny(condition func(K, V) bool) bool { + return map_.HasAny(m.elements, condition) } func addToMap[K comparable, V any](key K, val V, order []K, uniques map[K]V) []K { diff --git a/collection/immutable/ordered/map_/api.go b/collection/immutable/ordered/map_/api.go index b1436db3..abceec07 100644 --- a/collection/immutable/ordered/map_/api.go +++ b/collection/immutable/ordered/map_/api.go @@ -17,14 +17,6 @@ func New[K comparable, V any](order []K, elements map[K]V) ordered.Map[K, V] { return ordered.NewMapOf(order, elements) } -// From instantiates a map with key/values retrieved by the 'next' function. -// The next returns a key/value pairs with true or zero values with false if there are no more elements. -// -// Deprecated: replaced by [MapFromSeq2]. -func From[K comparable, V any](next func() (K, V, bool)) ordered.Map[K, V] { - return ordered.MapFromLoop(next) -} - // FromSeq2 creates a map with elements retrieved by the seq. func FromSeq2[K comparable, V any](seq seq.Seq2[K, V]) ordered.Map[K, V] { return ordered.MapFromSeq2(seq) diff --git a/collection/immutable/ordered/map_/test/map_test.go b/collection/immutable/ordered/map_/test/map_test.go index 10a060e9..6ba4fe41 100644 --- a/collection/immutable/ordered/map_/test/map_test.go +++ b/collection/immutable/ordered/map_/test/map_test.go @@ -9,7 +9,6 @@ import ( "github.com/m4gshm/gollections/collection/immutable/ordered" "github.com/m4gshm/gollections/collection/immutable/ordered/map_" "github.com/m4gshm/gollections/k" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -20,13 +19,8 @@ func Test_Map_Of(t *testing.T) { iterCheck(t, m) } -func Test_Map_From(t *testing.T) { - m := map_.From(loop.KeyValue(loop.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) - iterCheck(t, m) -} - func Test_Map_FromSeq(t *testing.T) { - m := map_.FromSeq2(seq.KeyValue(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) + m := map_.FromSeq2(seq.ToKV(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) iterCheck(t, m) } @@ -59,7 +53,7 @@ func Test_Map_Iterate_Keys(t *testing.T) { expectedK := slice.Of(1, 2, 4, 3) keys := []int{} - for it, key, ok := ordered.Keys().First(); ok; key, ok = it.Next() { + for key := range ordered.Keys().All { keys = append(keys, key) } assert.Equal(t, expectedK, keys) @@ -73,7 +67,7 @@ func Test_Map_Iterate_Values(t *testing.T) { expectedV := slice.Of("1", "2", "4", "3") values := []string{} - for it, val, ok := ordered.Values().First(); ok; val, ok = it.Next() { + for val := range ordered.Values().All { values = append(values, val) } @@ -91,42 +85,31 @@ func Test_Map_Zero(t *testing.T) { e := m.IsEmpty() assert.True(t, e) - head, _, _, ok := m.First() - assert.False(t, ok) - _, _, ok = head.Next() - assert.False(t, ok) - - head = m.Head() - _, _, ok = head.Next() + _, _, ok := m.Head() assert.False(t, ok) _, ok = m.Get("") assert.False(t, ok) - m.Track(nil) m.TrackEach(nil) m.Filter(nil) m.FilterKey(nil) m.FilterValue(nil) - m.Values().For(nil) m.Values().ForEach(nil) - m.ConvertValue(nil).Track(nil) + m.ConvertValue(nil).TrackEach(nil) m.ConvertValue(nil).Filter(nil).FilterKey(nil) m.ConvertValue(nil).Filter(nil).FilterValue(nil) - m.Keys().For(nil) m.Keys().ForEach(nil) - m.ConvertKey(nil).Track(nil) + m.ConvertKey(nil).TrackEach(nil) m.ConvertKey(nil).Filter(nil).FilterKey(nil) m.ConvertKey(nil).Filter(nil).FilterValue(nil) m.Convert(nil) - m.Sort(nil).Track(nil) m.Sort(nil).TrackEach(nil) - m.StableSort(nil).Track(nil) m.StableSort(nil).TrackEach(nil) } diff --git a/collection/immutable/ordered/map_iter.go b/collection/immutable/ordered/map_iter.go deleted file mode 100644 index 71bcd415..00000000 --- a/collection/immutable/ordered/map_iter.go +++ /dev/null @@ -1,106 +0,0 @@ -// Package ordered provides ordered map iterator implementations -package ordered - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/kv/collection" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/slice" -) - -// NewMapIter is the Iter constructor -func NewMapIter[K comparable, V any](uniques map[K]V, elements slice.Iter[K]) MapIter[K, V] { - return MapIter[K, V]{elements: elements, uniques: uniques} -} - -// MapIter is the ordered key/value pairs Iterator implementation -type MapIter[K comparable, V any] struct { - elements slice.Iter[K] - uniques map[K]V -} - -var _ collection.Iterator[string, any] = (*MapIter[string, any])(nil) - -// All is used to iterate through the iterator using `for ... range`. -func (i *MapIter[K, V]) All(consumer func(key K, value V) bool) { - kvloop.All(i.Next, consumer) -} - -// Track takes key, value pairs retrieved by the iterator. Can be interrupt by returning Break -func (i *MapIter[K, V]) Track(traker func(key K, value V) error) error { - return kvloop.Track(i.Next, traker) -} - -// TrackEach takes all key, value pairs retrieved by the iterator -func (i *MapIter[K, V]) TrackEach(traker func(key K, value V)) { - kvloop.TrackEach(i.Next, traker) -} - -// Next returns the next key/value pair. -// The ok result indicates whether the pair was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *MapIter[K, V]) Next() (key K, val V, ok bool) { - if i != nil { - if key, ok = i.elements.Next(); ok { - val = i.uniques[key] - } - } - return key, val, ok -} - -// Size returns the iterator capacity -func (i *MapIter[K, V]) Size() int { - return i.elements.Size() -} - -// NewValIter is default ValIter constructor -func NewValIter[K comparable, V any](elements []K, uniques map[K]V) *ValIter[K, V] { - return &ValIter[K, V]{elements: elements, uniques: uniques, current: slice.IterNoStarted} -} - -// ValIter is the Iteratoc over Map values -type ValIter[K comparable, V any] struct { - elements []K - uniques map[K]V - current int -} - -var ( - _ c.Iterator[any] = (*ValIter[int, any])(nil) - _ c.Sized = (*ValIter[int, any])(nil) -) - -// All is used to iterate through the iterator using `for ... range`. -func (i *ValIter[K, V]) All(consumer func(element V) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i *ValIter[K, V]) For(consumer func(element V) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach FlatIter all elements retrieved by the iterator -func (i *ValIter[K, V]) ForEach(consumer func(element V)) { - loop.ForEach(i.Next, consumer) -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *ValIter[K, V]) Next() (val V, ok bool) { - if i != nil && slice.HasNext(i.elements, i.current) { - i.current++ - return i.uniques[slice.Get(i.elements, i.current)], true - } - return val, false -} - -// Size returns the iterator capacity -func (i *ValIter[K, V]) Size() int { - if i == nil { - return 0 - } - return len(i.elements) -} diff --git a/collection/immutable/ordered/set.go b/collection/immutable/ordered/set.go index 884ec13f..0a0a4baf 100644 --- a/collection/immutable/ordered/set.go +++ b/collection/immutable/ordered/set.go @@ -3,10 +3,9 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -39,41 +38,14 @@ func (s Set[T]) IAll(consumer func(int, T) bool) { slice.TrackWhile(s.order, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) Loop() loop.Loop[T] { - return loop.Of(s.order...) +// Head returns the first element. +func (s Set[T]) Head() (T, bool) { + return collection.Head(s) } -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) Head() slice.Iter[T] { - return slice.NewHead(s.order) -} - -// Tail creates an iterator pointing to the end of the collection -// -// Deprecated: Tail is deprecated. Will be replaced by a rance-over function iterator. -func (s Set[T]) Tail() slice.Iter[T] { - return slice.NewTail(s.order) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) First() (*slice.Iter[T], T, bool) { - iterator := slice.NewHead(s.order) - return iterator.Crank() -} - -// Last returns the latest element of the collection, an iterator to reverse iterate over the remaining elements, and true\false marker of availability previous elements. -// If no more elements then ok==false. -func (s Set[T]) Last() (*slice.Iter[T], T, bool) { - iterator := slice.NewTail(s.order) - return iterator.CrankPrev() +// Tail returns the latest element. +func (s Set[T]) Tail() (T, bool) { + return slice.Tail(s.order) } // Slice collects the elements to a slice @@ -96,34 +68,29 @@ func (s Set[T]) IsEmpty() bool { return collection.IsEmpty(s) } -// For applies the 'consumer' function for every element until the consumer returns the c.Break to stop. -func (s Set[T]) For(consumer func(T) error) error { - return slice.For(s.order, consumer) -} - // ForEach applies the 'consumer' function for every element func (s Set[T]) ForEach(consumer func(T)) { slice.ForEach(s.order, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (s Set[T]) Filter(predicate func(T) bool) loop.Loop[T] { - return loop.Filter(s.Loop(), predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (s Set[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(s, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (s Set[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(s.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (s Set[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(s, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (s Set[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(s.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (s Set[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(s, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (s Set[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(s.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (s Set[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(s, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -131,9 +98,14 @@ func (s Set[T]) Reduce(merge func(T, T) T) T { return slice.Reduce(s.order, merge) } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (s Set[T]) HasAny(predicate func(T) bool) bool { - return slice.HasAny(s.order, predicate) +// HasAny checks whether the set contains an element that satisfies the condition. +func (s Set[T]) HasAny(condition func(T) bool) bool { + return slice.HasAny(s.order, condition) +} + +// First returns the first element that satisfies requirements of the condition. +func (s Set[T]) First(condition func(T) bool) (T, bool) { + return slice.First(s.order, condition) } // Contains checks is the collection contains an element diff --git a/collection/immutable/ordered/set/api.go b/collection/immutable/ordered/set/api.go index 83b159d1..7cc549bd 100644 --- a/collection/immutable/ordered/set/api.go +++ b/collection/immutable/ordered/set/api.go @@ -4,10 +4,8 @@ package set import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable/ordered" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" ) @@ -21,11 +19,6 @@ func New[T comparable](elements []T) ordered.Set[T] { return ordered.NewSet(elements...) } -// From instantiates a set with elements retrieved by the 'next' function -func From[T comparable](next func() (T, bool)) ordered.Set[T] { - return ordered.SetFromLoop(next) -} - // FromSeq instantiates a set with elements retrieved by the seq. func FromSeq[T comparable](seq seq.Seq[T]) ordered.Set[T] { return ordered.SetFromSeq(seq) @@ -36,22 +29,22 @@ func Sort[T comparable, f constraints.Ordered](s ordered.Set[T], by func(T) f) o return collection.Sort(s, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To comparable](set ordered.Set[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To comparable](set ordered.Set[From], converter func(From) To) seq.Seq[To] { return collection.Convert(set, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](set ordered.Set[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](set ordered.Set[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(set, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To comparable](set ordered.Set[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From, To comparable](set ordered.Set[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(set, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](set ordered.Set[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](set ordered.Set[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(set, flattener) } diff --git a/collection/immutable/ordered/set/test/set_test.go b/collection/immutable/ordered/set/test/set_test.go index 8d05ba2c..67b258ce 100644 --- a/collection/immutable/ordered/set/test/set_test.go +++ b/collection/immutable/ordered/set/test/set_test.go @@ -7,19 +7,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/m4gshm/gollections/collection/immutable/ordered/set" - "github.com/m4gshm/gollections/convert/ptr" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/convert/as" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" - "github.com/m4gshm/gollections/walk/group" ) -func Test_Set_From(t *testing.T) { - set := set.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 2, 3, 4), set.Slice()) -} - func Test_Set_FromSeq(t *testing.T) { set := set.FromSeq(seq.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) assert.Equal(t, slice.Of(1, 2, 3, 4), set.Slice()) @@ -34,11 +27,11 @@ func Test_Set_Iterate(t *testing.T) { expected := slice.Of(1, 2, 4, 3) assert.Equal(t, expected, values) - loopService := loop.Slice(ptr.Of(set.Head()).Next) + loopService := seq.Slice(set.All) assert.Equal(t, expected, loopService) out := make([]int, 0) - for it, v, ok := set.First(); ok; v, ok = it.Next() { + for v := range set.All { out = append(out, v) } assert.Equal(t, expected, out) @@ -69,22 +62,14 @@ func Test_Set_FilterMapReduce(t *testing.T) { assert.Equal(t, 12, s) } -func Test_Set_Group_By_Walker(t *testing.T) { - groups := group.Of(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7), func(e int) bool { return e%2 == 0 }) +func Test_Set_Group_By_Iterator(t *testing.T) { + groups := seq.Group(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7).All, func(e int) bool { return e%2 == 0 }, as.Is[int]) assert.Equal(t, len(groups), 2) assert.Equal(t, []int{1, 3, 7}, groups[false]) assert.Equal(t, []int{0, 2, 4, 6}, groups[true]) } -// func Test_Set_Group_By_Iterator(t *testing.T) { -// groups := loop.Group(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7).Loop(), func(e int) bool { return e%2 == 0 }).Map() - -// assert.Equal(t, len(groups), 2) -// assert.Equal(t, []int{1, 3, 7}, groups[false]) -// assert.Equal(t, []int{0, 2, 4, 6}, groups[true]) -// } - func Test_Set_Sort(t *testing.T) { var ( elements = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) @@ -111,7 +96,7 @@ func Test_Set_SortStructByField(t *testing.T) { func Test_Set_Convert(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) - strings = loop.Slice(loop.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) + strings = seq.Slice(seq.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) strings2 = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }).Slice() ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), strings) @@ -122,24 +107,11 @@ func Test_Set_Flatt(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) fints = set.Flat(ints, func(i int) []int { return slice.Of(i) }) - stringsPipe = loop.Filter(loop.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) + stringsPipe = seq.Filter(seq.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), stringsPipe.Slice()) } -func Test_Set_DoubleConvert(t *testing.T) { - var ( - ints = set.Of(3, 1, 5, 6, 8, 0, -2) - stringsPipe = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - prefixedStrinsPipe = loop.Convert(stringsPipe, func(s string) string { return "_" + s }) - ) - assert.Equal(t, slice.Of("_3", "_1", "_5", "_6", "_8", "_0"), prefixedStrinsPipe.Slice()) - - //second call do nothing - var no []string - assert.Equal(t, no, stringsPipe.Slice()) -} - type user struct { name string age int diff --git a/collection/immutable/ordered/values.go b/collection/immutable/ordered/values.go index 2988bd4c..26f2d88a 100644 --- a/collection/immutable/ordered/values.go +++ b/collection/immutable/ordered/values.go @@ -3,11 +3,11 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -28,39 +28,9 @@ var ( _ fmt.Stringer = (*MapValues[int, any])(nil) ) -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) Loop() loop.Loop[V] { - h := m.Head() - return h.Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) Head() *ValIter[K, V] { - var ( - order []K - elements map[K]V - ) - - order = m.order - elements = m.elements - - return NewValIter(order, elements) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) First() (*ValIter[K, V], V, bool) { - var ( - iterator = m.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok +// Head returns the first element. +func (m MapValues[K, V]) Head() (V, bool) { + return collection.Head(m) } // Len returns amount of elements @@ -97,11 +67,6 @@ func (m MapValues[K, V]) IAll(consumer func(int, V) bool) { map_.TrackOrderedValuesWhile(m.order, m.elements, consumer) } -// For applies the 'consumer' function for every value until the consumer returns the c.Break to stop. -func (m MapValues[K, V]) For(consumer func(V) error) error { - return map_.ForOrderedValues(m.order, m.elements, consumer) -} - // ForEach applies the 'consumer' function for every value func (m MapValues[K, V]) ForEach(consumer func(V)) { map_.ForEachOrderedValues(m.order, m.elements, consumer) @@ -119,24 +84,24 @@ func (m MapValues[K, V]) Get(index int) (V, bool) { return no, false } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapValues[K, V]) Filter(filter func(V) bool) loop.Loop[V] { - return loop.Filter(m.Loop(), filter) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapValues[K, V]) Filter(filter func(V) bool) seq.Seq[V] { + return collection.Filter(m, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapValues[K, V]) Filt(filter func(V) (bool, error)) breakLoop.Loop[V] { - return loop.Filt(m.Loop(), filter) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapValues[K, V]) Filt(filter func(V) (bool, error)) seq.SeqE[V] { + return collection.Filt(m, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m MapValues[K, V]) Convert(converter func(V) V) loop.Loop[V] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m MapValues[K, V]) Convert(converter func(V) V) seq.Seq[V] { + return collection.Convert(m, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m MapValues[K, V]) Conv(converter func(V) (V, error)) breakLoop.Loop[V] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m MapValues[K, V]) Conv(converter func(V) (V, error)) seq.SeqE[V] { + return collection.Conv(m, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -147,11 +112,15 @@ func (m MapValues[K, V]) Reduce(merge func(V, V) V) V { return v } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (m MapValues[K, V]) HasAny(predicate func(V) bool) bool { - return map_.HasAny(m.elements, func(_ K, v V) bool { - return predicate(v) - }) +// HasAny checks whether the collection contains a value that satisfies the condition. +func (m MapValues[K, V]) HasAny(condition func(V) bool) bool { + return map_.HasAny(m.elements, predicate.Value[K](condition)) +} + +// First returns the first key\value pair that satisfies the condition. +func (m MapValues[K, V]) First(condition func(V) bool) (V, bool) { + _, v, ok := map_.First(m.elements, predicate.Value[K](condition)) + return v, ok } func (m MapValues[K, V]) String() string { diff --git a/collection/immutable/set.go b/collection/immutable/set.go index a52da793..33a698af 100644 --- a/collection/immutable/set.go +++ b/collection/immutable/set.go @@ -3,11 +3,11 @@ package immutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable/ordered" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -33,31 +33,9 @@ func (s Set[T]) All(consumer func(T) bool) { map_.TrackKeysWhile(s.elements, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) Loop() loop.Loop[T] { - h := s.Head() - return (&h).Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) Head() map_.KeyIter[T, struct{}] { - return map_.NewKeyIter(s.elements) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Set.All]. -func (s Set[T]) First() (map_.KeyIter[T, struct{}], T, bool) { - var ( - iterator = s.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok +// Head returns the first element. +func (s Set[T]) Head() (T, bool) { + return collection.Head(s) } // Slice collects the elements to a slice @@ -80,35 +58,29 @@ func (s Set[T]) IsEmpty() bool { return collection.IsEmpty(s) } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func (s Set[T]) For(consumer func(T) error) error { - - return map_.ForKeys(s.elements, consumer) -} - // ForEach applies the 'consumer' function for every element func (s Set[T]) ForEach(consumer func(T)) { map_.ForEachKey(s.elements, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (s Set[T]) Filter(predicate func(T) bool) loop.Loop[T] { - return loop.Filter(s.Loop(), predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (s Set[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(s, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (s Set[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(s.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (s Set[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(s, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (s Set[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(s.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (s Set[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(s, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (s Set[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(s.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (s Set[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(s, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -119,11 +91,15 @@ func (s Set[T]) Reduce(merge func(T, T) T) T { return t } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (s Set[T]) HasAny(predicate func(T) bool) bool { - return map_.HasAny(s.elements, func(t T, _ struct{}) bool { - return predicate(t) - }) +// HasAny checks whether the set contains an element that satisfies the condition. +func (s Set[T]) HasAny(condition func(T) bool) bool { + return map_.HasAny(s.elements, predicate.Key[struct{}](condition)) +} + +// First returns the first element that satisfies the condition. +func (s Set[T]) First(condition func(T) bool) (T, bool) { + k, _, ok := map_.First(s.elements, predicate.Key[struct{}](condition)) + return k, ok } // Contains checks is the collection contains an element diff --git a/collection/immutable/set/api.go b/collection/immutable/set/api.go index 813b31a7..88f24ebc 100644 --- a/collection/immutable/set/api.go +++ b/collection/immutable/set/api.go @@ -4,11 +4,9 @@ package set import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable" "github.com/m4gshm/gollections/collection/immutable/ordered" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" ) @@ -22,14 +20,6 @@ func New[T comparable](elements []T) immutable.Set[T] { return immutable.NewSet(elements...) } -// From instantiates a map with key/values retrieved by the 'next' function. -// The next returns a key/value pairs with true or zero values with false if there are no more elements. -// -// Deprecated: replaced by [FromSeq]. -func From[T comparable](next func() (T, bool)) immutable.Set[T] { - return immutable.SetFromLoop(next) -} - // FromSeq creates a set with elements retrieved by the seq. func FromSeq[T comparable](seq seq.Seq[T]) immutable.Set[T] { return immutable.SetFromSeq(seq) @@ -40,22 +30,22 @@ func Sort[T comparable, f constraints.Ordered](s immutable.Set[T], by func(T) f) return collection.Sort(s, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To comparable](set immutable.Set[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To comparable](set immutable.Set[From], converter func(From) To) seq.Seq[To] { return collection.Convert(set, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](set immutable.Set[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](set immutable.Set[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(set, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To comparable](set immutable.Set[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From, To comparable](set immutable.Set[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(set, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](set immutable.Set[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](set immutable.Set[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(set, flattener) } diff --git a/collection/immutable/set/test/set_test.go b/collection/immutable/set/test/set_test.go index a00a6370..4a10f3a2 100644 --- a/collection/immutable/set/test/set_test.go +++ b/collection/immutable/set/test/set_test.go @@ -10,21 +10,13 @@ import ( oset "github.com/m4gshm/gollections/collection/immutable/ordered/set" "github.com/m4gshm/gollections/collection/immutable/set" "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/convert/ptr" "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/sort" - "github.com/m4gshm/gollections/walk/group" ) -func Test_Set_From(t *testing.T) { - set := set.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 2, 3, 4), sort.Asc(set.Slice())) -} - func Test_Set_FromSeq(t *testing.T) { set := set.FromSeq(seq.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) assert.Equal(t, slice.Of(1, 2, 3, 4), sort.Asc(set.Slice())) @@ -39,11 +31,11 @@ func Test_Set_Iterate(t *testing.T) { expected := slice.Of(1, 2, 3, 4) assert.Equal(t, expected, values) - loopSlice := sort.Asc(loop.Slice(ptr.Of(set.Head()).Next)) + loopSlice := sort.Asc(seq.Slice(set.All)) assert.Equal(t, expected, loopSlice) out := make(map[int]int, 0) - for it, v, ok := set.First(); ok; v, ok = it.Next() { + for v := range set.All { out[v] = v } @@ -76,18 +68,8 @@ func Test_Set_FilterMapReduce(t *testing.T) { assert.Equal(t, 12, s) } -func Test_Set_Group_By_Walker(t *testing.T) { - groups := group.Of(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7), func(e int) bool { return e%2 == 0 }) - - fg := sort.Asc(groups[false]) - tg := sort.Asc(groups[true]) - assert.Equal(t, len(groups), 2) - assert.Equal(t, []int{1, 3, 7}, fg) - assert.Equal(t, []int{0, 2, 4, 6}, tg) -} - func Test_Set_Group_By_Iterator(t *testing.T) { - groups := loop.Group(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7).Loop(), func(e int) bool { return e%2 == 0 }, as.Is[int]) + groups := seq.Group(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7).All, func(e int) bool { return e%2 == 0 }, as.Is[int]) assert.Equal(t, len(groups), 2) fg := sort.Asc(groups[false]) @@ -124,7 +106,7 @@ func Test_Set_SortStructByField(t *testing.T) { func Test_Set_Convert(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) - strings = sort.Asc(loop.Slice(loop.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 }))) + strings = sort.Asc(seq.Slice(seq.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 }))) strings2 = sort.Asc(set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }).Slice()) ) assert.Equal(t, slice.Of("0", "1", "3", "5", "6", "8"), strings) @@ -136,25 +118,12 @@ func Test_Set_Flatt(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) fints = set.Flat(ints, func(i int) []int { return slice.Of(i) }) - convFilt = loop.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - stringsPipe = loop.Filter(convFilt, func(s string) bool { return len(s) == 1 }) + convFilt = seq.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) + stringsPipe = seq.Filter(convFilt, func(s string) bool { return len(s) == 1 }) ) assert.Equal(t, slice.Of("0", "1", "3", "5", "6", "8"), sort.Asc(stringsPipe.Slice())) } -func Test_Set_DoubleConvert(t *testing.T) { - var ( - ints = set.Of(3, 1, 5, 6, 8, 0, -2) - stringsPipe = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - prefixedStrinsPipe = loop.Convert(stringsPipe, func(s string) string { return "_" + s }) - ) - assert.Equal(t, slice.Of("_0", "_1", "_3", "_5", "_6", "_8"), sort.Asc(prefixedStrinsPipe.Slice())) - - //second call do nothing - var no []string - assert.Equal(t, no, stringsPipe.Slice()) -} - func Test_Set_Zero(t *testing.T) { var set immutable.Set[int] @@ -163,7 +132,6 @@ func Test_Set_Zero(t *testing.T) { set.IsEmpty() set.Len() - set.For(nil) set.ForEach(nil) set.Slice() @@ -171,11 +139,7 @@ func Test_Set_Zero(t *testing.T) { set.Convert(nil) set.Filter(nil) - head := set.Head() - _, ok := head.Next() - assert.False(t, ok) - - _, _, ok = set.First() + _, ok := set.Head() assert.False(t, ok) } diff --git a/collection/immutable/test/keys_test.go b/collection/immutable/test/keys_test.go index 123ddd30..a0a8b984 100644 --- a/collection/immutable/test/keys_test.go +++ b/collection/immutable/test/keys_test.go @@ -5,14 +5,12 @@ import ( "github.com/m4gshm/gollections/collection/immutable" "github.com/m4gshm/gollections/collection/immutable/ordered" - "github.com/m4gshm/gollections/convert/ptr" "github.com/stretchr/testify/assert" ) func Test_MapKeys_Zero_Safety(t *testing.T) { var collection immutable.MapKeys[int, string] - collection.Loop() collection.Head() collection.Convert(func(i int) int { return i }) collection.Filter(func(_ int) bool { return true }) @@ -26,8 +24,6 @@ func Test_MapKeys_Zero_Safety(t *testing.T) { func Test_Map_Zero(t *testing.T) { var collection ordered.Map[int, string] - collection.Loop() - ptr.Of(collection.Head()).Next() collection.Convert(func(_ int, _ string) (int, string) { return 0, "" }) collection.Filter(func(_ int, _ string) bool { return true }) collection.Map() diff --git a/collection/immutable/values.go b/collection/immutable/values.go index ddbfb9de..f4118a75 100644 --- a/collection/immutable/values.go +++ b/collection/immutable/values.go @@ -3,10 +3,10 @@ package immutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -32,31 +32,9 @@ func (m MapValues[K, V]) All(consumer func(V) bool) { map_.TrackValuesWhile(m.elements, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) Loop() loop.Loop[V] { - h := m.Head() - return (&h).Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) Head() map_.ValIter[K, V] { - return map_.NewValIter(m.elements) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [MapValues.All]. -func (m MapValues[K, V]) First() (map_.ValIter[K, V], V, bool) { - var ( - iterator = m.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok +// Head returns the first element. +func (m MapValues[K, V]) Head() (V, bool) { + return collection.Head(m) } // Len returns amount of elements @@ -79,35 +57,29 @@ func (m MapValues[K, V]) Append(out []V) []V { return map_.AppendValues(m.elements, out) } -// For applies the 'consumer' function for collection values until the consumer returns the c.Break to stop. -func (m MapValues[K, V]) For(consumer func(V) error) error { - return map_.ForValues(m.elements, consumer) -} - // ForEach applies the 'consumer' function for every value of the collection func (m MapValues[K, V]) ForEach(consumer func(V)) { map_.ForEachValue(m.elements, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapValues[K, V]) Filter(filter func(V) bool) loop.Loop[V] { - h := m.Head() - return loop.Filter(h.Next, filter) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapValues[K, V]) Filter(filter func(V) bool) seq.Seq[V] { + return collection.Filter(m, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m MapValues[K, V]) Filt(predicate func(V) (bool, error)) breakLoop.Loop[V] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m MapValues[K, V]) Filt(filter func(V) (bool, error)) seq.SeqE[V] { + return collection.Filt(m, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m MapValues[K, V]) Convert(converter func(V) V) loop.Loop[V] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m MapValues[K, V]) Convert(converter func(V) V) seq.Seq[V] { + return collection.Convert(m, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m MapValues[K, V]) Conv(converter func(V) (V, error)) breakLoop.Loop[V] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m MapValues[K, V]) Conv(converter func(V) (V, error)) seq.SeqE[V] { + return collection.Conv(m, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -118,11 +90,15 @@ func (m MapValues[K, V]) Reduce(merge func(V, V) V) V { return v } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (m MapValues[K, V]) HasAny(predicate func(V) bool) bool { - return map_.HasAny(m.elements, func(_ K, v V) bool { - return predicate(v) - }) +// HasAny checks whether the collection contains a value that satisfies the condition. +func (m MapValues[K, V]) HasAny(condition func(V) bool) bool { + return map_.HasAny(m.elements, predicate.Value[K](condition)) +} + +// First returns the first key\value pair that satisfies the condition. +func (m MapValues[K, V]) First(condition func(V) bool) (V, bool) { + _, v, ok := map_.First(m.elements, predicate.Value[K](condition)) + return v, ok } // Sort creates a vector with sorted the values diff --git a/collection/immutable/vector.go b/collection/immutable/vector.go index a1d2fde3..bf03cd36 100644 --- a/collection/immutable/vector.go +++ b/collection/immutable/vector.go @@ -3,11 +3,10 @@ package immutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/notsafe" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -39,41 +38,14 @@ func (v Vector[T]) IAll(consumer func(int, T) bool) { slice.TrackWhile(v.elements, consumer) } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Vector.All]. -func (v Vector[T]) Loop() loop.Loop[T] { - return loop.Of(v.elements...) +// Head returns the first element. +func (v Vector[T]) Head() (T, bool) { + return collection.Head(v) } -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Vector.All]. -func (v Vector[T]) Head() slice.Iter[T] { - return slice.NewHead(v.elements) -} - -// Tail creates an iterator pointing to the end of the collection -// -// Deprecated: Tail is deprecated. Will be replaced by a rance-over function iterator. -func (v Vector[T]) Tail() slice.Iter[T] { - return slice.NewTail(v.elements) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Vector.All]. -func (v Vector[T]) First() (*slice.Iter[T], T, bool) { - h := slice.NewHead(v.elements) - return h.Crank() -} - -// Last returns the latest element of the collection, an iterator to reverse iterate over the remaining elements, and true\false marker of availability previous elements. -// If no more elements then ok==false. -func (v Vector[T]) Last() (*slice.Iter[T], T, bool) { - t := slice.NewTail(v.elements) - return t.CrankPrev() +// Tail returns the latest element. +func (v Vector[T]) Tail() (T, bool) { + return slice.Tail(v.elements) } // Slice collects the elements to a slice @@ -107,44 +79,34 @@ func (v Vector[T]) Get(index int) (out T, ok bool) { return slice.Gett(v.elements, index) } -// Track applies the 'consumer' function for elements until the consumer returns the c.Break to stop. -func (v Vector[T]) Track(consumer func(int, T) error) error { - return slice.Track(v.elements, consumer) -} - // TrackEach applies the 'consumer' function for every key/value pairs func (v Vector[T]) TrackEach(consumer func(int, T)) { slice.TrackEach(v.elements, consumer) } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func (v Vector[T]) For(consumer func(T) error) error { - return slice.For(v.elements, consumer) -} - // ForEach applies the 'consumer' function for every element func (v Vector[T]) ForEach(consumer func(T)) { slice.ForEach(v.elements, consumer) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (v Vector[T]) Filter(filter func(T) bool) loop.Loop[T] { - return loop.Filter(v.Loop(), filter) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (v Vector[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(v, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (v Vector[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(v.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (v Vector[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(v, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (v Vector[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(v.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (v Vector[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(v, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (v Vector[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(v.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (v Vector[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(v, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -152,9 +114,14 @@ func (v Vector[T]) Reduce(merge func(T, T) T) T { return slice.Reduce(v.elements, merge) } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (v Vector[T]) HasAny(predicate func(T) bool) bool { - return slice.HasAny(v.elements, predicate) +// HasAny checks whether the vector contains an element that satisfies the condition. +func (v Vector[T]) HasAny(condition func(T) bool) bool { + return slice.HasAny(v.elements, condition) +} + +// First returns the first element that satisfies requirements of the condition. +func (v Vector[T]) First(condition func(T) bool) (T, bool) { + return slice.First(v.elements, condition) } // Sort returns a sorted clone of the Vector diff --git a/collection/immutable/vector/api.go b/collection/immutable/vector/api.go index 0148d69c..6d4ee6fc 100644 --- a/collection/immutable/vector/api.go +++ b/collection/immutable/vector/api.go @@ -4,10 +4,9 @@ package vector import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/seq" ) // Of instantiates a vector with the specified elements @@ -25,33 +24,27 @@ func Wrap[T any](elements []T) immutable.Vector[T] { return immutable.WrapVector(elements) } -// From instantiates a vector with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -func From[T any](next func() (T, bool)) immutable.Vector[T] { - return immutable.VectorFromLoop(next) -} - // Sort copy the specified vector with sorted elements func Sort[T any, F constraints.Ordered](v immutable.Vector[T], by func(T) F) immutable.Vector[T] { - return collection.Sort[immutable.Vector[T]](v, by) + return collection.Sort(v, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To any](vector immutable.Vector[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To any](vector immutable.Vector[From], converter func(From) To) seq.Seq[To] { return collection.Convert(vector, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](vector immutable.Vector[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](vector immutable.Vector[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(vector, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From any, To any](vector immutable.Vector[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From any, To any](vector immutable.Vector[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(vector, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](vector immutable.Vector[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](vector immutable.Vector[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(vector, flattener) } diff --git a/collection/immutable/vector/test/vector_test.go b/collection/immutable/vector/test/vector_test.go index 665fa11a..aa33e27d 100644 --- a/collection/immutable/vector/test/vector_test.go +++ b/collection/immutable/vector/test/vector_test.go @@ -8,73 +8,19 @@ import ( "github.com/m4gshm/gollections/collection/immutable" "github.com/m4gshm/gollections/collection/immutable/vector" + "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" ) -func Test_Vector_From(t *testing.T) { - set := vector.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 1, 2, 2, 3, 4, 3, 2, 1), set.Slice()) -} - func Test_VectorIterate(t *testing.T) { expected := slice.Of(1, 2, 3, 4) v := vector.Of(1, 2, 3, 4) result := make([]int, v.Len()) i := 0 - for it := v.Head(); it.HasNext(); { - result[i] = it.GetNext() - i++ - } - assert.Equal(t, expected, result) -} - -func Test_VectorIterate2(t *testing.T) { - expected := slice.Of(1, 2, 3, 4) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - for it, v, ok := v.First(); ok; v, ok = it.Next() { - result[i] = v - i++ - } - assert.Equal(t, expected, result) -} - -func Test_VectorIterate3(t *testing.T) { - expected := slice.Of(1, 2, 3, 4) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - it := v.Head() - for v, ok := it.Next(); ok; v, ok = it.Next() { - result[i] = v - i++ - } - assert.Equal(t, expected, result) -} - -func Test_VectorReverseIteration(t *testing.T) { - expected := slice.Of(4, 3, 2, 1) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - for it := v.Tail(); it.HasPrev(); { - result[i] = it.GetPrev() - i++ - } - assert.Equal(t, expected, result) -} - -func Test_VectorReverseIteration2(t *testing.T) { - expected := slice.Of(4, 3, 2, 1) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - for it, v, ok := v.Last(); ok; v, ok = it.Prev() { - result[i] = v + for it := range v.All { + result[i] = it i++ } assert.Equal(t, expected, result) @@ -106,7 +52,7 @@ func Test_Vector_SortStructByField(t *testing.T) { func Test_Vector_Convert(t *testing.T) { var ( ints = vector.Of(3, 1, 5, 6, 8, 0, -2) - strings = loop.Slice(loop.Filter(vector.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) + strings = seq.Slice(seq.Filter(vector.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) strings2 = vector.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }).Slice() ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), strings) @@ -117,57 +63,30 @@ func Test_Vector_Flatt(t *testing.T) { var ( deepInts = vector.Of(vector.Of(3, 1), vector.Of(5, 6, 8, 0, -2)) ints = vector.Flat(deepInts, immutable.Vector[int].Slice) - c = loop.Convert(ints, strconv.Itoa) - stringsPipe = loop.Filter(c.Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) + c = seq.Convert(ints, strconv.Itoa) + stringsPipe = c.Filter(func(s string) bool { return len(s) == 1 }) ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), stringsPipe.Slice()) } -func Test_Vector_DoubleConvert(t *testing.T) { - var ( - ints = vector.Of(3, 1, 5, 6, 8, 0, -2) - stringsPipe = vector.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - prefixedStrinsPipe = loop.Convert(stringsPipe, func(s string) string { return "_" + s }) - ) - assert.Equal(t, slice.Of("_3", "_1", "_5", "_6", "_8", "_0"), prefixedStrinsPipe.Slice()) - - //second call do nothing - var no []string - assert.Equal(t, no, stringsPipe.Slice()) -} - func Test_Vector_Zero(t *testing.T) { var vec immutable.Vector[int] vec.IsEmpty() vec.Len() - vec.For(nil) vec.ForEach(nil) - vec.Track(nil) vec.TrackEach(nil) vec.Slice() - head := vec.Head() - assert.False(t, head.HasNext()) - assert.False(t, head.HasPrev()) + _, ok := vec.Head() - _, ok := head.Get() assert.False(t, ok) - _, ok = head.Next() - assert.False(t, ok) - head.Size() - tail := vec.Tail() - assert.False(t, tail.HasNext()) - assert.False(t, tail.HasPrev()) + _, ok = vec.Tail() - _, ok = tail.Get() - assert.False(t, ok) - _, ok = tail.Next() assert.False(t, ok) - tail.Size() } type user struct { diff --git a/collection/mutable/api.go b/collection/mutable/api.go index 6db635da..aa39f267 100644 --- a/collection/mutable/api.go +++ b/collection/mutable/api.go @@ -4,8 +4,6 @@ package mutable import ( "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection/mutable/ordered" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/seq2" @@ -27,14 +25,6 @@ func NewSetCap[T comparable](capacity int) *Set[T] { return WrapSet(make(map[T]struct{}, capacity)) } -// SetFromLoop creates a set with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [SetFromSeq]. -func SetFromLoop[T comparable](next func() (T, bool)) *Set[T] { - return SetFromSeq(loop.Loop[T](next).All) -} - // SetFromSeq creates a set with elements retrieved by the seq. func SetFromSeq[T comparable](seq seq.Seq[T]) *Set[T] { if seq == nil { @@ -67,13 +57,6 @@ func NewMapOf[K comparable, V any](elements map[K]V) *Map[K, V] { return MapFromSeq2(seq2.OfMap(elements)) } -// MapFromLoop creates a map with elements retrieved converter the 'next' function. -// -// Deprecated: replaced by [MapFromSeq2]. -func MapFromLoop[K comparable, V any](next func() (K, V, bool)) *Map[K, V] { - return MapFromSeq2(kvloop.Loop[K, V](next).All) -} - // MapFromSeq2 creates a map with elements retrieved by the seq. func MapFromSeq2[K comparable, V any](seq seq.Seq2[K, V]) *Map[K, V] { if seq == nil { @@ -96,14 +79,6 @@ func NewVectorCap[T any](capacity int) *Vector[T] { return WrapVector(make([]T, 0, capacity)) } -// VectorFromLoop creates a vector with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [VectorFromLoop]. -func VectorFromLoop[T any](next func() (T, bool)) *Vector[T] { - return WrapVector(loop.Slice(next)) -} - // VectorFromSeq creates a vector with elements retrieved by the seq. func VectorFromSeq[T any](s seq.Seq[T]) *Vector[T] { return WrapVector(seq.Slice(s)) diff --git a/collection/mutable/map.go b/collection/mutable/map.go index ab4eaa57..6a46e8c4 100644 --- a/collection/mutable/map.go +++ b/collection/mutable/map.go @@ -3,17 +3,17 @@ package mutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/kv/loop" - breakMapFilter "github.com/m4gshm/gollections/break/kv/predicate" - breakMapConvert "github.com/m4gshm/gollections/break/map_/convert" + converte "github.com/m4gshm/gollections/break/kv/convert" + filtere "github.com/m4gshm/gollections/break/kv/predicate" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable" "github.com/m4gshm/gollections/collection/mutable/ordered" "github.com/m4gshm/gollections/kv/convert" - "github.com/m4gshm/gollections/kv/loop" - filter "github.com/m4gshm/gollections/kv/predicate" + kvfilter "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" + "github.com/m4gshm/gollections/seq2" "github.com/m4gshm/gollections/slice" ) @@ -45,39 +45,9 @@ func (m *Map[K, V]) All(consumer func(K, V) bool) { } } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) Loop() loop.Loop[K, V] { - h := m.Head() - return h.Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) Head() map_.Iter[K, V] { - var out map[K]V - if m != nil { - out = *m - } - return map_.NewIter(out) -} - -// First returns the first key/value pair of the map, an iterator to iterate over the remaining pair, and true\false marker of availability next pairs. -// If no more then ok==false. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) First() (map_.Iter[K, V], K, V, bool) { - var out map[K]V - if m != nil { - out = *m - } - var ( - iterator = map_.NewIter(out) - firstK, firstV, ok = iterator.Next() - ) - return iterator, firstK, firstV, ok +// Head returns the first key\value pair. +func (m *Map[K, V]) Head() (K, V, bool) { + return seq2.Head(m.All) } // Map collects the key/value pairs into a new map @@ -118,14 +88,6 @@ func (m *Map[K, V]) IsEmpty() bool { return collection.IsEmpty(m) } -// Track applies the 'consumer' function for all key/value pairs until the consumer returns the c.Break to stop. -func (m *Map[K, V]) Track(consumer func(K, V) error) error { - if m == nil { - return nil - } - return map_.Track(*m, consumer) -} - // TrackEach applies the 'consumer' function for every key/value pairs func (m *Map[K, V]) TrackEach(consumer func(K, V)) { if m != nil { @@ -226,64 +188,64 @@ func (m *Map[K, V]) String() string { return map_.ToString(out) } -// FilterKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FilterKey(predicate func(K) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Key[V](predicate)) +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterKey(filter func(K) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Key[V](filter)) } -// FiltKey returns a breakable loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltKey(predicate func(K) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Key[V](predicate)) +// FiltKey returns an errorable seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltKey(filter func(K) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Key[V](filter)) } -// ConvertKey returns a loop that applies the 'converter' function to keys of the map -func (m *Map[K, V]) ConvertKey(converter func(K) K) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Key[V](converter)) +// ConvertKey returns a seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvertKey(converter func(K) K) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Key[V](converter)) } -// ConvKey returns a breabkable stream that applies the 'converter' function to keys of the map -func (m Map[K, V]) ConvKey(converter func(K) (K, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Key[V](converter)) +// ConvKey returns an errorable seq that applies the 'converter' function to keys of the map +func (m Map[K, V]) ConvKey(converter func(K) (K, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Key[V](converter)) } -// FilterValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FilterValue(predicate func(V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Value[K](predicate)) +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FilterValue(filter func(V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Value[K](filter)) } -// FiltValue returns a breakable loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FiltValue(predicate func(V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Value[K](predicate)) +// FiltValue returns an errorable seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m Map[K, V]) FiltValue(filter func(V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Value[K](filter)) } -// ConvertValue returns a loop that applies the 'converter' function to values of the map -func (m *Map[K, V]) ConvertValue(converter func(V) V) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Value[K](converter)) +// ConvertValue returns a seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvertValue(converter func(V) V) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Value[K](converter)) } -// ConvValue returns a breakable loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvValue(converter func(V) (V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Value[K](converter)) +// ConvValue returns an errorable seq that applies the 'converter' function to values of the map +func (m Map[K, V]) ConvValue(converter func(V) (V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Value[K](converter)) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m *Map[K, V]) Filter(predicate func(K, V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filter(filter func(K, V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m *Map[K, V]) Filt(predicate func(K, V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m Map[K, V]) Filt(filter func(K, V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m *Map[K, V]) Convert(converter func(K, V) (K, V)) loop.Loop[K, V] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Convert(converter func(K, V) (K, V)) seq.Seq2[K, V] { + return seq2.Convert(m.All, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m *Map[K, V]) Conv(converter func(K, V) (K, V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m Map[K, V]) Conv(converter func(K, V) (K, V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converter) } // Reduce reduces the key/value pairs of the map into an one pair using the 'merge' function @@ -294,10 +256,10 @@ func (m *Map[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (k K, v V) { return k, v } -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func (m *Map[K, V]) HasAny(predicate func(K, V) bool) bool { +// HasAny checks whether the map contains a key\value pair that satisfies the condition. +func (m *Map[K, V]) HasAny(condition func(K, V) bool) bool { if m != nil { - return map_.HasAny(*m, predicate) + return map_.HasAny(*m, condition) } return false } diff --git a/collection/mutable/map_/api.go b/collection/mutable/map_/api.go index e068b61e..734ff4ee 100644 --- a/collection/mutable/map_/api.go +++ b/collection/mutable/map_/api.go @@ -22,13 +22,6 @@ func New[K comparable, V any](capacity int) *mutable.Map[K, V] { return mutable.NewMapCap[K, V](capacity) } -// From instantiates a map with elements obtained by passing the 'loop' function. -// -// Deprecated: replaced by [FromSeq2]. -func From[K comparable, V any](next func() (K, V, bool)) *mutable.Map[K, V] { - return mutable.MapFromLoop(next) -} - // FromSeq2 creates a map with elements retrieved by the seq. func FromSeq2[K comparable, V any](seq seq.Seq2[K, V]) *mutable.Map[K, V] { return mutable.MapFromSeq2(seq) diff --git a/collection/mutable/map_/test/map_test.go b/collection/mutable/map_/test/map_test.go index 5b4cb2be..d7841074 100644 --- a/collection/mutable/map_/test/map_test.go +++ b/collection/mutable/map_/test/map_test.go @@ -12,7 +12,6 @@ import ( "github.com/m4gshm/gollections/collection/mutable/ordered" "github.com/m4gshm/gollections/convert/as" "github.com/m4gshm/gollections/k" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -23,13 +22,8 @@ func Test_Map_Of(t *testing.T) { iterCheck(t, m) } -func Test_Map_From(t *testing.T) { - m := map_.From(loop.KeyValue(loop.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) - iterCheck(t, m) -} - func Test_Map_FromSeq(t *testing.T) { - m := map_.FromSeq2(seq.KeyValue(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) + m := map_.FromSeq2(seq.ToKV(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) iterCheck(t, m) } @@ -127,24 +121,18 @@ func Test_Map_Nil(t *testing.T) { e := m.IsEmpty() assert.True(t, e) - head, _, _, ok := m.First() - assert.False(t, ok) - - head = m.Head() - _, _, ok = head.Next() + _, _, ok := m.Head() assert.False(t, ok) m.Reduce(nil) - m.Convert(nil).Track(nil) - m.ConvertKey(nil).Filter(nil) - m.ConvertValue(nil).Filter(nil) - m.Filter(nil).Convert(nil).Track(nil) + // m.Convert(nil).TrackEach(nil) + // m.ConvertKey(nil).Filter(nil) + // m.ConvertValue(nil).Filter(nil) + // m.Filter(nil).Convert(nil).TrackEach(nil) - m.Keys().For(nil) m.Keys().ForEach(nil) - m.Values().For(nil) m.Values().ForEach(nil) - m.Values().Convert(nil).For(nil) + // m.Values().Convert(nil).ForEach(nil) m.Values().Filter(nil).ForEach(nil) } @@ -172,24 +160,18 @@ func Test_Map_Zero(t *testing.T) { l := m.Len() assert.Equal(t, 1, l) - head, k, v, ok := m.First() + k, v, ok := m.Head() assert.True(t, ok) assert.Equal(t, "d", k) assert.Equal(t, "D", v) - head = m.Head() - _, _, ok = head.Next() - assert.True(t, ok) - m.Reduce(func(k1, v1, k2, v2 string) (string, string) { return k1 + k2, v1 + v2 }) - m.Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) - m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) + m.Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) + m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) - m.Keys().For(func(_ string) error { return nil }) m.Keys().ForEach(func(_ string) {}) - m.Values().For(func(_ string) error { return nil }) m.Values().ForEach(func(_ string) {}) - m.Values().Convert(as.Is[string]).For(func(_ string) error { return nil }) + m.Values().Convert(as.Is[string]).ForEach(func(_ string) {}) m.Values().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) } @@ -217,25 +199,19 @@ func Test_Map_new(t *testing.T) { l := m.Len() assert.Equal(t, 1, l) - head, k, v, ok := m.First() + k, v, ok := m.Head() assert.True(t, ok) assert.Equal(t, "d", k) assert.Equal(t, "D", v) - head = m.Head() - _, _, ok = head.Next() - assert.True(t, ok) - m.Reduce(func(k1, v1, k2, v2 string) (string, string) { return k1 + k2, v1 + v2 }) - m.Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) + m.Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) - m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) + m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) - m.Keys().For(func(_ string) error { return nil }) m.Keys().ForEach(func(_ string) {}) - m.Values().For(func(_ string) error { return nil }) m.Values().ForEach(func(_ string) {}) - m.Values().Convert(as.Is[string]).For(func(_ string) error { return nil }) + m.Values().Convert(as.Is[string]).ForEach(func(_ string) {}) m.Values().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) } diff --git a/collection/mutable/ordered/api.go b/collection/mutable/ordered/api.go index 0014c411..5625cc4a 100644 --- a/collection/mutable/ordered/api.go +++ b/collection/mutable/ordered/api.go @@ -3,8 +3,6 @@ package ordered import ( "github.com/m4gshm/gollections/c" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice/clone" ) @@ -19,14 +17,6 @@ func NewSetCap[T comparable](capacity int) *Set[T] { return WrapSet(make([]T, 0, capacity), make(map[T]int, capacity)) } -// SetFromLoop creates a set with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [SetFromSeq]. -func SetFromLoop[T comparable](next func() (T, bool)) *Set[T] { - return SetFromSeq((loop.Loop[T])(next).All) -} - // SetFromSeq creates a set with elements retrieved by the seq. func SetFromSeq[T comparable](seq seq.Seq[T]) *Set[T] { if seq == nil { @@ -70,13 +60,6 @@ func NewMapOf[K comparable, V any](order []K, elements map[K]V) *Map[K, V] { return WrapMap(clone.Of(order), uniques) } -// MapFromLoop creates a map with elements retrieved converter the 'next' function. -// -// Deprecated: replaced by [MapFromSeq2]. -func MapFromLoop[K comparable, V any](next func() (K, V, bool)) *Map[K, V] { - return MapFromSeq2(kvloop.Loop[K, V](next).All) -} - // MapFromSeq2 creates a map with elements retrieved by the seq. func MapFromSeq2[K comparable, V any](seq seq.Seq2[K, V]) *Map[K, V] { if seq == nil { diff --git a/collection/mutable/ordered/map.go b/collection/mutable/ordered/map.go index 6863552e..8e346506 100644 --- a/collection/mutable/ordered/map.go +++ b/collection/mutable/ordered/map.go @@ -3,16 +3,16 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/kv/loop" - breakMapFilter "github.com/m4gshm/gollections/break/kv/predicate" - breakMapConvert "github.com/m4gshm/gollections/break/map_/convert" + converte "github.com/m4gshm/gollections/break/kv/convert" + filtere "github.com/m4gshm/gollections/break/kv/predicate" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/immutable/ordered" "github.com/m4gshm/gollections/kv/convert" - "github.com/m4gshm/gollections/kv/loop" - filter "github.com/m4gshm/gollections/kv/predicate" + kvfilter "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" + "github.com/m4gshm/gollections/seq" + "github.com/m4gshm/gollections/seq2" "github.com/m4gshm/gollections/slice" ) @@ -44,54 +44,9 @@ func (m *Map[K, V]) All(consumer func(K, V) bool) { } } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) Loop() loop.Loop[K, V] { - h := m.Head() - return h.Next -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) Head() ordered.MapIter[K, V] { - var ( - order []K - elements map[K]V - ) - if m != nil { - elements = m.elements - order = m.order - } - return ordered.NewMapIter(elements, slice.NewHead(order)) -} - -// Tail creates an iterator pointing to the end of the collection -// -// Deprecated: Tail is deprecated. Will be replaced by a rance-over function iterator. -func (m *Map[K, V]) Tail() ordered.MapIter[K, V] { - var ( - order []K - elements map[K]V - ) - if m != nil { - elements = m.elements - order = m.order - } - return ordered.NewMapIter(elements, slice.NewTail(order)) -} - -// First returns the first key/value pair of the map, an iterator to iterate over the remaining pair, and true\false marker of availability next pairs. -// If no more then ok==false. -// -// Deprecated: replaced by [Map.All]. -func (m *Map[K, V]) First() (ordered.MapIter[K, V], K, V, bool) { - var ( - iterator = m.Head() - firstK, firstV, ok = iterator.Next() - ) - return iterator, firstK, firstV, ok +// Head returns the first key\value pair. +func (m *Map[K, V]) Head() (K, V, bool) { + return seq2.Head(m.All) } // Map collects the key/value pairs into a new map @@ -132,14 +87,6 @@ func (m *Map[K, V]) IsEmpty() bool { return collection.IsEmpty(m) } -// Track applies the 'consumer' function for all key/value pairs until the consumer returns the c.Break to stop. -func (m *Map[K, V]) Track(consumer func(K, V) error) error { - if m == nil { - return nil - } - return map_.TrackOrdered(m.order, m.elements, consumer) -} - // TrackEach applies the 'consumer' function for every key/value pairs func (m *Map[K, V]) TrackEach(consumer func(K, V)) { if m == nil { @@ -236,64 +183,64 @@ func (m *Map[K, V]) String() string { return map_.ToStringOrdered(order, elements) } -// FilterKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FilterKey(predicate func(K) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Key[V](predicate)) +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m *Map[K, V]) FilterKey(filter func(K) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Key[V](filter)) } -// FiltKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (m Map[K, V]) FiltKey(predicate func(K) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Key[V](predicate)) +// FiltKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function +func (m *Map[K, V]) FiltKey(filter func(K) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Key[V](filter)) } -// ConvertKey returns a loop that applies the 'converter' function to keys of the map -func (m *Map[K, V]) ConvertKey(converter func(K) K) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Key[V](converter)) +// ConvertKey returns a seq that applies the 'converter' function to keys of the map +func (m *Map[K, V]) ConvertKey(converter func(K) K) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Key[V](converter)) } -// ConvKey returns a loop that applies the 'converter' function to keys of the map -func (m *Map[K, V]) ConvKey(converter func(K) (K, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Key[V](converter)) +// ConvKey returns a seq that applies the 'converter' function to keys of the map +func (m *Map[K, V]) ConvKey(converter func(K) (K, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Key[V](converter)) } -// FilterValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FilterValue(predicate func(V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), filter.Value[K](predicate)) +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m *Map[K, V]) FilterValue(filter func(V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, kvfilter.Value[K](filter)) } -// FiltValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (m *Map[K, V]) FiltValue(predicate func(V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), breakMapFilter.Value[K](predicate)) +// FiltValue returns an errorable seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function +func (m *Map[K, V]) FiltValue(filter func(V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filtere.Value[K](filter)) } -// ConvertValue returns a loop that applies the 'converter' function to values of the map -func (m *Map[K, V]) ConvertValue(converter func(V) V) loop.Loop[K, V] { - return loop.Convert(m.Loop(), convert.Value[K](converter)) +// ConvertValue returns a seq that applies the 'converter' function to values of the map +func (m *Map[K, V]) ConvertValue(converter func(V) V) seq.Seq2[K, V] { + return seq2.Convert(m.All, convert.Value[K](converter)) } -// ConvValue returns a loop that applies the 'converter' function to values of the map -func (m Map[K, V]) ConvValue(converter func(V) (V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), breakMapConvert.Value[K](converter)) +// ConvValue returns an errorable seq that applies the 'converter' function to values of the map +func (m *Map[K, V]) ConvValue(converter func(V) (V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converte.Value[K](converter)) } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (m *Map[K, V]) Filter(predicate func(K, V) bool) loop.Loop[K, V] { - return loop.Filter(m.Loop(), predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (m *Map[K, V]) Filter(filter func(K, V) bool) seq.Seq2[K, V] { + return seq2.Filter(m.All, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (m *Map[K, V]) Filt(predicate func(K, V) (bool, error)) breakLoop.Loop[K, V] { - return loop.Filt(m.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (m *Map[K, V]) Filt(filter func(K, V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Filt(m.All, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (m *Map[K, V]) Convert(converter func(K, V) (K, V)) loop.Loop[K, V] { - return loop.Convert(m.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (m *Map[K, V]) Convert(converter func(K, V) (K, V)) seq.Seq2[K, V] { + return seq2.Convert(m.All, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (m *Map[K, V]) Conv(converter func(K, V) (K, V, error)) breakLoop.Loop[K, V] { - return loop.Conv(m.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (m *Map[K, V]) Conv(converter func(K, V) (K, V, error)) seq.SeqE[c.KV[K, V]] { + return seq2.Conv(m.All, converter) } // Reduce reduces the key/value pairs of the map into an one pair using the 'merge' function @@ -304,9 +251,9 @@ func (m *Map[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (k K, v V) { return k, v } -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func (m *Map[K, V]) HasAny(predicate func(K, V) bool) bool { - return map_.HasAny(m.elements, predicate) +// HasAny checks whether the map contains a key\value pair that satisfies the condition. +func (m *Map[K, V]) HasAny(condition func(K, V) bool) bool { + return map_.HasAny(m.elements, condition) } // Immutable converts to an immutable map instance diff --git a/collection/mutable/ordered/map_/api.go b/collection/mutable/ordered/map_/api.go index bcfe8710..5994d34d 100644 --- a/collection/mutable/ordered/map_/api.go +++ b/collection/mutable/ordered/map_/api.go @@ -22,13 +22,6 @@ func New[K comparable, V any](capacity int) *ordered.Map[K, V] { return ordered.WrapMap(make([]K, 0, capacity), make(map[K]V, capacity)) } -// From instantiates a map with elements obtained by passing the 'loop' function. -// -// Deprecated: replaced by [MapFromSeq2]. -func From[K comparable, V any](next func() (K, V, bool)) *ordered.Map[K, V] { - return ordered.MapFromLoop(next) -} - // FromSeq2 creates a map with elements retrieved by the seq. func FromSeq2[K comparable, V any](seq seq.Seq2[K, V]) *ordered.Map[K, V] { return ordered.MapFromSeq2(seq) diff --git a/collection/mutable/ordered/map_/test/map_test.go b/collection/mutable/ordered/map_/test/map_test.go index 41de9f98..6796024a 100644 --- a/collection/mutable/ordered/map_/test/map_test.go +++ b/collection/mutable/ordered/map_/test/map_test.go @@ -10,7 +10,6 @@ import ( "github.com/m4gshm/gollections/collection/mutable/ordered" omap "github.com/m4gshm/gollections/collection/mutable/ordered/map_" "github.com/m4gshm/gollections/k" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -21,13 +20,8 @@ func Test_Map_Of(t *testing.T) { iterCheck(t, m) } -func Test_Map_From(t *testing.T) { - m := omap.From(loop.KeyValue(loop.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) - iterCheck(t, m) -} - func Test_Map_FromSeq(t *testing.T) { - m := omap.FromSeq2(seq.KeyValue(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) + m := omap.FromSeq2(seq.ToKV(seq.Of(k.V(1, "1"), k.V(1, "1"), k.V(2, "2"), k.V(4, "4"), k.V(3, "3"), k.V(1, "1")), c.KV[int, string].Key, c.KV[int, string].Value)) iterCheck(t, m) } @@ -81,28 +75,21 @@ func Test_Map_Nil(t *testing.T) { e := m.IsEmpty() assert.True(t, e) - head, _, _, ok := m.First() - assert.False(t, ok) - - head = m.Head() - _, _, ok = head.Next() + _, _, ok := m.Head() assert.False(t, ok) - m.Track(nil) m.TrackEach(nil) m.Reduce(nil) - m.Convert(nil).Track(nil) - m.ConvertKey(nil).FiltKey(nil) - m.ConvertKey(nil).Track(nil) - m.ConvertValue(nil).Track(nil) - m.Filter(nil).Convert(nil).Track(nil) + m.Convert(nil).TrackEach(nil) + // m.ConvertKey(nil).FiltKey(nil) + m.ConvertKey(nil).TrackEach(nil) + m.ConvertValue(nil).TrackEach(nil) + m.Filter(nil).Convert(nil).TrackEach(nil) - m.Keys().For(nil) m.Keys().ForEach(nil) - m.Values().For(nil) m.Values().ForEach(nil) - m.Values().Convert(nil).For(nil) + // m.Values().Convert(nil).For(nil) m.Values().Filter(nil).ForEach(nil) } @@ -125,35 +112,28 @@ func Test_Map_Zero(t *testing.T) { e := m.IsEmpty() assert.False(t, e) - head, k, v, ok := m.First() + k, v, ok := m.Head() assert.True(t, ok) assert.Equal(t, "a", k) assert.Equal(t, "A", v) - head = m.Head() - _, _, ok = head.Next() - assert.True(t, ok) - - m.Track(func(_, _ string) error { return nil }) m.TrackEach(func(_, _ string) {}) m.Reduce(func(k1, v1, k2, v2 string) (string, string) { return k1 + k2, v1 + v2 }) - m.Convert(func(_, _ string) (string, string) { return k, v }).Track(func(_, _ string) error { return nil }) - m.ConvertKey(func(s string) string { return s }).Track(func(_, _ string) error { return nil }) - m.ConvertValue(func(s string) string { return s }).Track(func(_, _ string) error { return nil }) - m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) + m.Convert(func(_, _ string) (string, string) { return k, v }).TrackEach(func(_, _ string) {}) + m.ConvertKey(func(s string) string { return s }).TrackEach(func(_, _ string) {}) + m.ConvertValue(func(s string) string { return s }).TrackEach(func(_, _ string) {}) + m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) - m.Keys().For(func(_ string) error { return nil }) m.Keys().ForEach(func(_ string) {}) m.Keys().Convert(func(s string) string { return s }).Slice() - m.Keys().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) + // m.Keys().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) m.Keys().Filter(func(_ string) bool { return true }).Slice() m.Keys().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) - m.Values().For(func(_ string) error { return nil }) m.Values().ForEach(func(_ string) {}) m.Values().Convert(func(s string) string { return s }).Slice() - m.Values().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) + // m.Values().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) m.Values().Filter(func(_ string) bool { return true }).Slice() m.Values().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) } @@ -176,35 +156,28 @@ func Test_Map_new(t *testing.T) { e := m.IsEmpty() assert.False(t, e) - head, k, v, ok := m.First() + k, v, ok := m.Head() assert.True(t, ok) assert.Equal(t, "a", k) assert.Equal(t, "A", v) - head = m.Head() - _, _, ok = head.Next() - assert.True(t, ok) - - m.Track(func(_, _ string) error { return nil }) m.TrackEach(func(_, _ string) {}) m.Reduce(func(k1, v1, k2, v2 string) (string, string) { return k1 + k2, v1 + v2 }) - m.Convert(func(_, _ string) (string, string) { return k, v }).Track(func(_, _ string) error { return nil }) - m.ConvertKey(func(s string) string { return s }).Track(func(_, _ string) error { return nil }) - m.ConvertValue(func(s string) string { return s }).Track(func(_, _ string) error { return nil }) - m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).Track(func(_, _ string) error { return nil }) + m.Convert(func(_, _ string) (string, string) { return k, v }).TrackEach(func(_, _ string) {}) + m.ConvertKey(func(s string) string { return s }).TrackEach(func(_, _ string) {}) + m.ConvertValue(func(s string) string { return s }).TrackEach(func(_, _ string) {}) + m.Filter(func(_, _ string) bool { return true }).Convert(func(s1, s2 string) (string, string) { return s1, s2 }).TrackEach(func(_, _ string) {}) - m.Keys().For(func(_ string) error { return nil }) m.Keys().ForEach(func(_ string) {}) m.Keys().Convert(func(s string) string { return s }).Slice() - m.Keys().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) + m.Keys().Convert(func(s string) string { return s }).ForEach(func(_ string) {}) m.Keys().Filter(func(_ string) bool { return true }).Slice() m.Keys().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) - m.Values().For(func(_ string) error { return nil }) m.Values().ForEach(func(_ string) {}) m.Values().Convert(func(s string) string { return s }).Slice() - m.Values().Convert(func(s string) string { return s }).For(func(_ string) error { return nil }) + m.Values().Convert(func(s string) string { return s }).ForEach(func(_ string) {}) m.Values().Filter(func(_ string) bool { return true }).Slice() m.Values().Filter(func(_ string) bool { return true }).ForEach(func(_ string) {}) } diff --git a/collection/mutable/ordered/set.go b/collection/mutable/ordered/set.go index 3f94a830..e9d35cc0 100644 --- a/collection/mutable/ordered/set.go +++ b/collection/mutable/ordered/set.go @@ -3,10 +3,8 @@ package ordered import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -49,43 +47,12 @@ func (s *Set[T]) IAll(consumer func(int, T) bool) { } } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) Loop() loop.Loop[T] { +// Head returns the first element. +func (s *Set[T]) Head() (t T, ok bool) { if s == nil { - return nil + return t, false } - return loop.Of((*s.order)...) -} - -// IterEdit creates iterator that can delete iterable elements -func (s *Set[T]) IterEdit() c.DelIterator[T] { - h := s.Head() - return &h -} - -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) Head() SetIter[T] { - var elements *[]T - if s != nil { - elements = s.order - } - return NewSetIter(elements, s.DeleteOne) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) First() (SetIter[T], T, bool) { - var ( - iterator = s.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok + return collection.Head(s) } // Slice collects the elements to a slice @@ -245,18 +212,6 @@ func (s *Set[T]) DeleteActualOne(element T) bool { return false } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func (s *Set[T]) For(consumer func(T) error) error { - if s == nil { - return nil - } - order := s.order - if order == nil { - return nil - } - return slice.For(*order, consumer) -} - // ForEach applies the 'consumer' function for every element func (s *Set[T]) ForEach(consumer func(T)) { if s != nil { @@ -266,25 +221,24 @@ func (s *Set[T]) ForEach(consumer func(T)) { } } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (s *Set[T]) Filter(predicate func(T) bool) loop.Loop[T] { - h := s.Head() - return loop.Filter(h.Next, predicate) +// Filter returns a seq consisting of elements that satisfy the condition of the 'filter' function +func (s *Set[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(s, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (s *Set[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(s.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (s *Set[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(s, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (s *Set[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(s.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (s *Set[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(s, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (s *Set[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(s.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (s *Set[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(s, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -297,16 +251,26 @@ func (s *Set[T]) Reduce(merge func(T, T) T) (t T) { return t } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (s *Set[K]) HasAny(predicate func(K) bool) bool { +// HasAny checks whether the set contains an element that satisfies the condition. +func (s *Set[T]) HasAny(condition func(T) bool) bool { if s != nil { if order := s.order; order != nil { - return slice.HasAny(*order, predicate) + return slice.HasAny(*order, condition) } } return false } +// First returns the first element that satisfies requirements of the condition. +func (s *Set[T]) First(condition func(T) bool) (t T, ok bool) { + if s != nil { + if order := s.order; order != nil { + return slice.First(*order, condition) + } + } + return t, false +} + // Sort sorts the elements func (s *Set[T]) Sort(comparer slice.Comparer[T]) *Set[T] { return s.sortBy(slice.Sort, comparer) diff --git a/collection/mutable/ordered/set/api.go b/collection/mutable/ordered/set/api.go index ba5eaa8a..85c9b58c 100644 --- a/collection/mutable/ordered/set/api.go +++ b/collection/mutable/ordered/set/api.go @@ -4,10 +4,8 @@ package set import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/mutable/ordered" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" ) @@ -16,11 +14,6 @@ func Of[T comparable](elements ...T) *ordered.Set[T] { return ordered.NewSet(elements...) } -// From instantiates a set with elements retrieved by the 'next' function -func From[T comparable](next func() (T, bool)) *ordered.Set[T] { - return ordered.SetFromLoop(next) -} - // FromSeq creates a set with elements retrieved by the seq. func FromSeq[T comparable](seq seq.Seq[T]) *ordered.Set[T] { return ordered.SetFromSeq(seq) @@ -41,22 +34,22 @@ func Sort[T comparable, O constraints.Ordered](s *ordered.Set[T], by func(T) O) return collection.Sort(s, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To comparable](set *ordered.Set[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To comparable](set *ordered.Set[From], converter func(From) To) seq.Seq[To] { return collection.Convert(set, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](set *ordered.Set[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](set *ordered.Set[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(set, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To comparable](set *ordered.Set[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From, To comparable](set *ordered.Set[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(set, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](set *ordered.Set[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](set *ordered.Set[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(set, flattener) } diff --git a/collection/mutable/ordered/set/test/set_test.go b/collection/mutable/ordered/set/test/set_test.go index 232d5095..ef1498ec 100644 --- a/collection/mutable/ordered/set/test/set_test.go +++ b/collection/mutable/ordered/set/test/set_test.go @@ -8,21 +8,13 @@ import ( "github.com/m4gshm/gollections/collection/mutable/ordered" "github.com/m4gshm/gollections/collection/mutable/ordered/set" - "github.com/m4gshm/gollections/convert/ptr" "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/predicate/eq" "github.com/m4gshm/gollections/slice" - "github.com/m4gshm/gollections/walk/group" ) -func Test_Set_From(t *testing.T) { - set := set.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 2, 3, 4), set.Slice()) -} - func Test_Set_FromSeq(t *testing.T) { set := set.FromSeq(seq.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) assert.Equal(t, slice.Of(1, 2, 3, 4), set.Slice()) @@ -37,15 +29,11 @@ func Test_Set_Iterate(t *testing.T) { expected := slice.Of(1, 2, 4, 3) assert.Equal(t, expected, values) - iterSlice := loop.Slice(set.Loop()) + iterSlice := seq.Slice(set.All) assert.Equal(t, expected, iterSlice) - loopSlice := loop.Slice(ptr.Of(set.Head()).Next) - assert.Equal(t, expected, loopSlice) - out := make([]int, 0) - next := set.Loop() - for v, ok := next(); ok; v, ok = next() { + for v := range set.All { out = append(out, v) } assert.Equal(t, expected, out) @@ -101,37 +89,15 @@ func Test_Set_Delete(t *testing.T) { assert.Equal(t, 0, len(set.Slice())) } -func Test_Set_DeleteByIterator(t *testing.T) { - set := set.Of(1, 1, 2, 4, 3, 1) - iterator := set.IterEdit() - - i := 0 - for _, ok := iterator.Next(); ok; _, ok = iterator.Next() { - i++ - iterator.Delete() - } - - assert.Equal(t, 4, i) - assert.Equal(t, 0, len(set.Slice())) -} - func Test_Set_FilterMapReduce(t *testing.T) { s := set.Of(1, 1, 2, 4, 3, 1).Filter(func(i int) bool { return i%2 == 0 }).Convert(func(i int) int { return i * 2 }).Reduce(op.Sum[int]) assert.Equal(t, 12, s) } -func Test_Set_Group(t *testing.T) { - groups := group.Of(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7), func(e int) bool { return e%2 == 0 }) - - assert.Equal(t, len(groups), 2) - assert.Equal(t, []int{1, 3, 7}, groups[false]) - assert.Equal(t, []int{0, 2, 4, 6}, groups[true]) -} - func Test_Set_Convert(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) - strings = loop.Slice[string](loop.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) + strings = seq.Slice(seq.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 })) strings2 = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }).Slice() ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), strings) @@ -142,24 +108,11 @@ func Test_Set_Flatt(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) fints = set.Flat(ints, func(i int) []int { return slice.Of(i) }) - stringsPipe = loop.Filter(loop.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) + stringsPipe = seq.Filter(seq.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) ) assert.Equal(t, slice.Of("3", "1", "5", "6", "8", "0"), stringsPipe.Slice()) } -func Test_Set_DoubleConvert(t *testing.T) { - var ( - ints = set.Of(3, 1, 5, 6, 8, 0, -2) - stringsPipe = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - prefixedStrinsPipe = loop.Convert(stringsPipe, func(s string) string { return "_" + s }) - ) - assert.Equal(t, slice.Of("_3", "_1", "_5", "_6", "_8", "_0"), prefixedStrinsPipe.Slice()) - - //second call do nothing - var no []string - assert.Equal(t, no, stringsPipe.Slice()) -} - func Test_Set_Nil(t *testing.T) { var set *ordered.Set[int] var nils []int @@ -175,15 +128,12 @@ func Test_Set_Nil(t *testing.T) { set.IsEmpty() set.Len() - _ = set.For(nil) set.ForEach(nil) set.Slice() - head := set.Head() - _, ok := head.Next() + _, ok := set.Head() assert.False(t, ok) - head.Delete() } func Test_Set_Zero(t *testing.T) { @@ -204,13 +154,10 @@ func Test_Set_Zero(t *testing.T) { assert.True(t, mset.IsEmpty()) assert.Equal(t, 0, mset.Len()) - mset.For(nil) mset.ForEach(nil) - head := mset.Head() - _, ok := head.Next() + _, ok := mset.Head() assert.False(t, ok) - head.Delete() } func Test_Set_new(t *testing.T) { @@ -232,13 +179,10 @@ func Test_Set_new(t *testing.T) { assert.True(t, mset.IsEmpty()) assert.Equal(t, 0, mset.Len()) - mset.For(nil) mset.ForEach(nil) - head := mset.Head() - _, ok := head.Next() + _, ok := mset.Head() assert.False(t, ok) - head.Delete() } func Test_Set_CopyByValue(t *testing.T) { diff --git a/collection/mutable/ordered/set_iter.go b/collection/mutable/ordered/set_iter.go deleted file mode 100644 index bae5d354..00000000 --- a/collection/mutable/ordered/set_iter.go +++ /dev/null @@ -1,70 +0,0 @@ -package ordered - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/slice" -) - -// NewSetIter creates a set's iterator. -func NewSetIter[T any](elements *[]T, del func(v T)) SetIter[T] { - return SetIter[T]{elements: elements, current: slice.IterNoStarted, del: del} -} - -// SetIter set iterator -type SetIter[T any] struct { - elements *[]T - current int - del func(v T) -} - -var ( - _ c.Iterator[any] = (*SetIter[any])(nil) - _ c.DelIterator[any] = (*SetIter[any])(nil) -) - -// All is used to iterate through the collection using `for e := range`. -func (i *SetIter[T]) All(consumer func(element T) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i *SetIter[T]) For(consumer func(element T) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach takes all elements retrieved by the iterator. -func (i *SetIter[T]) ForEach(consumer func(element T)) { - loop.ForEach(i.Next, consumer) -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *SetIter[T]) Next() (t T, ok bool) { - if !(i == nil || i.elements == nil) { - if slice.HasNext(*i.elements, i.current) { - i.current++ - return slice.Gett(*i.elements, i.current) - } - } - return t, ok -} - -// Size returns the iterator capacity -func (i *SetIter[T]) Size() (capacity int) { - if !(i == nil || i.elements == nil) { - capacity = len(*i.elements) - } - return capacity -} - -// Delete deletes the current element -func (i *SetIter[T]) Delete() { - if !(i == nil || i.elements == nil) { - if v, ok := slice.Gett(*i.elements, i.current); ok { - i.current-- - i.del(v) - } - } -} diff --git a/collection/mutable/set.go b/collection/mutable/set.go index 10dbe126..6158310f 100644 --- a/collection/mutable/set.go +++ b/collection/mutable/set.go @@ -3,11 +3,10 @@ package mutable import ( "fmt" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/mutable/ordered" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/kv/predicate" "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -36,48 +35,23 @@ var ( // All is used to iterate through the collection using `for e := range`. func (s *Set[T]) All(consumer func(T) bool) { + if s == nil { + return + } for v := range s.elements { if !consumer(v) { return } } -} - -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) Loop() loop.Loop[T] { - h := s.Head() - return (&h).Next -} -// IterEdit creates iterator that can delete iterable elements -func (s *Set[T]) IterEdit() c.DelIterator[T] { - h := s.Head() - return &h } -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) Head() SetIter[T] { - var elements map[T]struct{} - if s != nil { - elements = s.elements +// Head returns the first element. +func (s *Set[T]) Head() (t T, ok bool) { + if s == nil { + return t, false } - return NewSetIter(elements, s.DeleteOne) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Set.All]. -func (s *Set[T]) First() (SetIter[T], T, bool) { - var ( - iterator = s.Head() - first, ok = iterator.Next() - ) - return iterator, first, ok + return collection.Head(s) } // Slice collects the elements to a slice @@ -230,14 +204,6 @@ func (s *Set[T]) DeleteActualOne(element T) (ok bool) { return ok } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func (s *Set[T]) For(consumer func(T) error) error { - if s == nil { - return nil - } - return map_.ForKeys(s.elements, consumer) -} - // ForEach applies the 'consumer' function for every element func (s *Set[T]) ForEach(consumer func(T)) { if s != nil { @@ -245,24 +211,24 @@ func (s *Set[T]) ForEach(consumer func(T)) { } } -// Filter returns a loop consisting of elements that satisfy the condition of the 'predicate' function -func (s *Set[T]) Filter(predicate func(T) bool) loop.Loop[T] { - return loop.Filter(s.Loop(), predicate) +// Filter returns a seq that checks elements by the 'filter' function and returns successful ones. +func (s *Set[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(s, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (s Set[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(s.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (s *Set[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(s, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (s *Set[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(s.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (s *Set[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(s, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (s *Set[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(s.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (s *Set[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(s, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -275,16 +241,23 @@ func (s *Set[T]) Reduce(merge func(T, T) T) (t T) { return t } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (s *Set[K]) HasAny(predicate func(K) bool) bool { +// HasAny checks whether the set contains an element that satisfies the condition. +func (s *Set[K]) HasAny(condition func(K) bool) bool { if s != nil { - return map_.HasAny(s.elements, func(k K, _ struct{}) bool { - return predicate(k) - }) + return map_.HasAny(s.elements, predicate.Key[struct{}](condition)) } return false } +// First returns the element that satisfies the condition. +func (s *Set[T]) First(condition func(T) bool) (t T, ok bool) { + if s != nil { + t, _, ok := map_.First(s.elements, predicate.Key[struct{}](condition)) + return t, ok + } + return t, false +} + // Sort transforms to the ordered Set contains sorted elements func (s *Set[T]) Sort(comparer slice.Comparer[T]) *ordered.Set[T] { if s != nil { diff --git a/collection/mutable/set/api.go b/collection/mutable/set/api.go index 79fde279..e382684c 100644 --- a/collection/mutable/set/api.go +++ b/collection/mutable/set/api.go @@ -4,11 +4,9 @@ package set import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/mutable" "github.com/m4gshm/gollections/collection/mutable/ordered" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" ) @@ -17,13 +15,6 @@ func Of[T comparable](elements ...T) *mutable.Set[T] { return mutable.NewSet(elements...) } -// From instantiates a set with elements retrieved by the 'next' function. -// -// Deprecated: replaced by [FromSeq]. -func From[T comparable](next func() (T, bool)) *mutable.Set[T] { - return mutable.SetFromLoop(next) -} - // FromSeq creates a set with elements retrieved by the seq. func FromSeq[T comparable](seq seq.Seq[T]) *mutable.Set[T] { return mutable.SetFromSeq(seq) @@ -44,22 +35,22 @@ func Sort[T comparable, F constraints.Ordered](s *mutable.Set[T], by func(T) F) return collection.Sort(s, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To comparable](set *mutable.Set[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To comparable](set *mutable.Set[From], converter func(From) To) seq.Seq[To] { return collection.Convert(set, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](set *mutable.Set[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](set *mutable.Set[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(set, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To comparable](set *mutable.Set[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From, To comparable](set *mutable.Set[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(set, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](set *mutable.Set[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](set *mutable.Set[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(set, flattener) } diff --git a/collection/mutable/set/test/set_test.go b/collection/mutable/set/test/set_test.go index 3ecfb1fb..4690368a 100644 --- a/collection/mutable/set/test/set_test.go +++ b/collection/mutable/set/test/set_test.go @@ -11,18 +11,11 @@ import ( "github.com/m4gshm/gollections/collection/mutable/set" "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/sort" - "github.com/m4gshm/gollections/walk/group" ) -func Test_Set_From(t *testing.T) { - set := set.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 2, 3, 4), sort.Asc(set.Slice())) -} - func Test_Set_FromSeq(t *testing.T) { set := set.FromSeq(seq.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) assert.Equal(t, slice.Of(1, 2, 3, 4), sort.Asc(set.Slice())) @@ -37,12 +30,11 @@ func Test_Set_Iterate(t *testing.T) { expected := slice.Of(1, 2, 3, 4) assert.Equal(t, expected, values) - loopSlice := sort.Asc(loop.Slice(set.Loop())) + loopSlice := sort.Asc(seq.Slice(set.All)) assert.Equal(t, expected, loopSlice) out := make(map[int]int, 0) - next := set.Loop() - for v, ok := next(); ok; v, ok = next() { + for v := range set.All { out[v] = v } @@ -107,20 +99,6 @@ func Test_Set_Delete(t *testing.T) { assert.Equal(t, 0, len(set.Slice())) } -func Test_Set_DeleteByIterator(t *testing.T) { - set := set.Of(1, 1, 2, 4, 3, 1) - loopator := set.IterEdit() - - i := 0 - for _, ok := loopator.Next(); ok; _, ok = loopator.Next() { - i++ - loopator.Delete() - } - - assert.Equal(t, 4, i) - assert.Equal(t, 0, len(set.Slice())) -} - func Test_Set_Contains(t *testing.T) { set := set.Of(1, 1, 2, 4, 3, 1) assert.True(t, set.Contains(1)) @@ -136,20 +114,10 @@ func Test_Set_FilterMapReduce(t *testing.T) { assert.Equal(t, 12, s) } -func Test_Set_Group_By_Walker(t *testing.T) { - groups := group.Of(set.Of(0, 1, 1, 2, 4, 3, 1, 6, 7), func(e int) bool { return e%2 == 0 }) - - fg := sort.Asc(groups[false]) - tg := sort.Asc(groups[true]) - assert.Equal(t, len(groups), 2) - assert.Equal(t, []int{1, 3, 7}, fg) - assert.Equal(t, []int{0, 2, 4, 6}, tg) -} - func Test_Set_Convert(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) - strings = sort.Asc(loop.Slice[string](loop.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 }))) + strings = sort.Asc(seq.Slice(seq.Filter(set.Convert(ints, strconv.Itoa), func(s string) bool { return len(s) == 1 }))) strings2 = sort.Asc(set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }).Slice()) ) assert.Equal(t, slice.Of("0", "1", "3", "5", "6", "8"), strings) @@ -160,25 +128,11 @@ func Test_Set_Flatt(t *testing.T) { var ( ints = set.Of(3, 3, 1, 1, 1, 5, 6, 8, 8, 0, -2, -2) fints = set.Flat(ints, func(i int) []int { return slice.Of(i) }) - stringsPipe = loop.Filter(loop.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }), func(s string) bool { return len(s) == 1 }) + stringsPipe = seq.Convert(fints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) ) assert.Equal(t, slice.Of("0", "1", "3", "5", "6", "8"), sort.Asc(stringsPipe.Slice())) } -func Test_Set_DoubleConvert(t *testing.T) { - var ( - ints = set.Of(3, 1, 5, 6, 8, 0, -2) - stringsPipe = set.Convert(ints, strconv.Itoa).Filter(func(s string) bool { return len(s) == 1 }) - prefixedStrinsPipe = loop.Convert(stringsPipe, func(s string) string { return "_" + s }) - ) - s := prefixedStrinsPipe.Slice() - assert.Equal(t, slice.Of("_0", "_1", "_3", "_5", "_6", "_8"), sort.Asc(s)) - - //second call do nothing - var no []string - assert.Equal(t, no, stringsPipe.Slice()) -} - func Test_Set_Nil(t *testing.T) { var set *mutable.Set[int] var nils []int @@ -194,15 +148,12 @@ func Test_Set_Nil(t *testing.T) { set.IsEmpty() set.Len() - _ = set.For(nil) set.ForEach(nil) set.Slice() - head := set.Head() - _, ok := head.Next() + _, ok := set.Head() assert.False(t, ok) - head.Delete() } func Test_Set_Zero(t *testing.T) { @@ -224,13 +175,10 @@ func Test_Set_Zero(t *testing.T) { assert.True(t, mset.IsEmpty()) assert.Equal(t, 0, mset.Len()) - mset.For(nil) mset.ForEach(nil) - head := mset.Head() - _, ok := head.Next() + _, ok := mset.Head() assert.False(t, ok) - head.Delete() } func Test_Set_new(t *testing.T) { @@ -252,13 +200,10 @@ func Test_Set_new(t *testing.T) { assert.True(t, mset.IsEmpty()) assert.Equal(t, 0, mset.Len()) - mset.For(nil) mset.ForEach(nil) - head := mset.Head() - _, ok := head.Next() + _, ok := mset.Head() assert.False(t, ok) - head.Delete() } func Test_Set_Sort(t *testing.T) { diff --git a/collection/mutable/set_iter.go b/collection/mutable/set_iter.go deleted file mode 100644 index 3130b396..00000000 --- a/collection/mutable/set_iter.go +++ /dev/null @@ -1,41 +0,0 @@ -package mutable - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/map_" -) - -// NewSetIter creates SetIter instance. -func NewSetIter[K comparable](uniques map[K]struct{}, del func(element K)) SetIter[K] { - return SetIter[K]{KeyIter: map_.NewKeyIter(uniques), del: del} -} - -// SetIter is the Set Iterator implementation. -type SetIter[K comparable] struct { - map_.KeyIter[K, struct{}] - del func(element K) - currentKey K - ok bool -} - -var ( - _ c.Iterator[int] = (*SetIter[int])(nil) - _ c.DelIterator[int] = (*SetIter[int])(nil) -) - -// Next returns the next element if it exists -func (i *SetIter[K]) Next() (key K, ok bool) { - if i != nil { - key, _, ok = i.Iter.Next() - i.currentKey = key - i.ok = ok - } - return key, ok -} - -// Delete deletes the current element -func (i *SetIter[K]) Delete() { - if i != nil && i.ok { - i.del(i.currentKey) - } -} diff --git a/collection/mutable/slice_iter.go b/collection/mutable/slice_iter.go deleted file mode 100644 index 7323efd8..00000000 --- a/collection/mutable/slice_iter.go +++ /dev/null @@ -1,160 +0,0 @@ -package mutable - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/slice" -) - -// NewHead instantiates Iter starting at the first element of a slice. -func NewHead[TS ~[]T, T any](elements *TS, del func(int) bool) *SliceIter[T] { - if elements == nil { - return nil - } - return &SliceIter[T]{elements: slice.UpcastRef(elements), current: slice.IterNoStarted, del: del} -} - -// NewTail instantiates Iter starting at the last element of a slice. -func NewTail[TS ~[]T, T any](elements *TS, del func(int) bool) *SliceIter[T] { - if elements == nil { - return nil - } - return &SliceIter[T]{elements: slice.UpcastRef(elements), current: len(*elements), del: del} -} - -// SliceIter is the Iterator implementation for mutable containers. -type SliceIter[T any] struct { - elements *[]T - current, step int - del func(index int) bool -} - -var ( - _ c.Iterator[any] = (*SliceIter[any])(nil) - _ c.PrevIterator[any] = (*SliceIter[any])(nil) - _ c.DelIterator[any] = (*SliceIter[any])(nil) -) - -// All is used to iterate through the collection using `for e := range`. -func (i *SliceIter[T]) All(consumer func(element T) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i *SliceIter[T]) For(consumer func(element T) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach FlatIter all elements retrieved by the iterator -func (i *SliceIter[T]) ForEach(consumer func(element T)) { - loop.ForEach(i.Next, consumer) -} - -// HasNext checks the next element existing -func (i *SliceIter[T]) HasNext() bool { - if i == nil || i.elements == nil { - return false - } - return slice.HasNext(*i.elements, i.current) -} - -// HasPrev checks the previous element existing -func (i *SliceIter[T]) HasPrev() bool { - if i == nil || i.elements == nil { - return false - } - return slice.HasPrev(*i.elements, i.current) -} - -// GetNext returns the next element -func (i *SliceIter[T]) GetNext() (t T) { - if i != nil { - t, _ = i.Next() - } - return -} - -// GetPrev returns the previous element -func (i *SliceIter[T]) GetPrev() (t T) { - if i != nil { - t, _ = i.Prev() - } - return -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *SliceIter[T]) Next() (T, bool) { - if i.HasNext() { - i.current++ - i.step = 1 - return slice.Get(*i.elements, i.current), true - } - var no T - return no, false -} - -// Prev returns the previous element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *SliceIter[T]) Prev() (T, bool) { - if i.HasPrev() { - i.current-- - i.step = 0 - return slice.Get(*i.elements, i.current), true - } - var no T - return no, false -} - -// Get returns the current element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *SliceIter[T]) Get() (t T, ok bool) { - if i == nil || i.elements == nil { - return t, ok - } - current := i.current - elements := *i.elements - if slice.IsValidIndex(len(elements), current) { - return elements[current], true - } - return t, ok -} - -// Size returns the iterator capacity -func (i *SliceIter[T]) Size() int { - if i == nil || i.elements == nil { - return 0 - } - return len(*i.elements) -} - -// Delete deletes the current element -func (i *SliceIter[T]) Delete() { - if i == nil { - return - } else if deleted := i.del(i.current); deleted { - i.current -= i.step - } -} - -// DeleteNext deletes the next element if it exists -func (i *SliceIter[T]) DeleteNext() bool { - if i == nil { - return false - } - return i.del(i.current + 1) -} - -// DeletePrev deletes the previos element if it exists -func (i *SliceIter[T]) DeletePrev() bool { - if i == nil { - return false - } else if deleted := i.del(i.current - 1); deleted { - i.current-- - return true - } - return false -} diff --git a/collection/mutable/vector.go b/collection/mutable/vector.go index 03796545..b96d1b25 100644 --- a/collection/mutable/vector.go +++ b/collection/mutable/vector.go @@ -4,10 +4,8 @@ import ( "fmt" "sort" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/collection" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/notsafe" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" @@ -48,50 +46,20 @@ func (v *Vector[T]) IAll(consumer func(int, T) bool) { } } -// Loop creates a loop to iterate through the collection. -// -// Deprecated: replaced by [Vector.All]. -func (v *Vector[T]) Loop() loop.Loop[T] { +// Head returns the first element. +func (v *Vector[T]) Head() (t T, ok bool) { if v == nil { - return nil + return t, false } - return loop.Of(*v...) + return collection.Head(v) } -// Head creates an iterator to iterate through the collection. -// -// Deprecated: replaced by [Vector.All]. -func (v *Vector[T]) Head() *SliceIter[T] { - return NewHead(v, v.DeleteActualOne) -} - -// Tail creates an iterator pointing to the end of the collection -// -// Deprecated: Tail is deprecated. Will be replaced by a rance-over function iterator. -func (v *Vector[T]) Tail() *SliceIter[T] { - return NewTail(v, v.DeleteActualOne) -} - -// First returns the first element of the collection, an iterator to iterate over the remaining elements, and true\false marker of availability next elements. -// If no more elements then ok==false. -// -// Deprecated: replaced by [Vector.All]. -func (v *Vector[T]) First() (*SliceIter[T], T, bool) { - var ( - iterator = NewHead(v, v.DeleteActualOne) - first, ok = iterator.Next() - ) - return iterator, first, ok -} - -// Last returns the latest element of the collection, an iterator to reverse iterate over the remaining elements, and true\false marker of availability previous elements. -// If no more elements then ok==false. -func (v *Vector[T]) Last() (*SliceIter[T], T, bool) { - var ( - iterator = NewTail(v, v.DeleteActualOne) - first, ok = iterator.Prev() - ) - return iterator, first, ok +// Tail returns the latest element +func (v *Vector[T]) Tail() (t T, ok bool) { + if v == nil { + return t, false + } + return slice.Tail(*v) } // Slice collects the elements to a slice @@ -128,14 +96,6 @@ func (v *Vector[T]) Len() int { return notsafe.GetLen(*v) } -// Track applies consumer to elements with error checking until the consumer returns the c.Break to stop.tracking. -func (v *Vector[T]) Track(consumer func(int, T) error) error { - if v == nil { - return nil - } - return slice.Track(*v, consumer) -} - // TrackEach applies consumer to elements without error checking func (v *Vector[T]) TrackEach(consumer func(int, T)) { if v != nil { @@ -143,14 +103,6 @@ func (v *Vector[T]) TrackEach(consumer func(int, T)) { } } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func (v *Vector[T]) For(consumer func(T) error) error { - if v == nil { - return nil - } - return slice.For(*v, consumer) -} - // ForEach applies consumer to elements without error checking func (v *Vector[T]) ForEach(consumer func(T)) { if !(v == nil) { @@ -222,9 +174,10 @@ func (v *Vector[T]) DeleteActual(indexes ...int) bool { return false } l := len(indexes) - if l == 0 { + switch l { + case 0: return false - } else if l == 1 { + case 1: return v.DeleteActualOne(indexes[0]) } @@ -291,24 +244,24 @@ func (v *Vector[T]) SetNew(index int, value T) bool { return true } -// Filter returns a loop consisting of vector elements matching the filter -func (v *Vector[T]) Filter(filter func(T) bool) loop.Loop[T] { - return loop.Filter(v.Loop(), filter) +// Filter returns a seq consisting of vector elements matching the filter +func (v *Vector[T]) Filter(filter func(T) bool) seq.Seq[T] { + return collection.Filter(v, filter) } -// Filt returns a breakable loop consisting of elements that satisfy the condition of the 'predicate' function -func (v *Vector[T]) Filt(predicate func(T) (bool, error)) breakLoop.Loop[T] { - return loop.Filt(v.Loop(), predicate) +// Filt returns an errorable seq consisting of elements that satisfy the condition of the 'filter' function +func (v *Vector[T]) Filt(filter func(T) (bool, error)) seq.SeqE[T] { + return collection.Filt(v, filter) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func (v *Vector[T]) Convert(converter func(T) T) loop.Loop[T] { - return loop.Convert(v.Loop(), converter) +// Convert returns a seq that applies the 'converter' function to the collection elements +func (v *Vector[T]) Convert(converter func(T) T) seq.Seq[T] { + return collection.Convert(v, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func (v *Vector[T]) Conv(converter func(T) (T, error)) breakLoop.Loop[T] { - return loop.Conv(v.Loop(), converter) +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func (v *Vector[T]) Conv(converter func(T) (T, error)) seq.SeqE[T] { + return collection.Conv(v, converter) } // Reduce reduces the elements into an one using the 'merge' function @@ -319,14 +272,22 @@ func (v *Vector[T]) Reduce(merge func(T, T) T) (out T) { return out } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (v *Vector[T]) HasAny(predicate func(T) bool) (ok bool) { +// HasAny checks whether the vector contains an element that satisfies the condition. +func (v *Vector[T]) HasAny(condition func(T) bool) (ok bool) { if v != nil { - ok = slice.HasAny(*v, predicate) + ok = slice.HasAny(*v, condition) } return ok } +// First returns the first element that satisfies requirements of the condition. +func (v *Vector[T]) First(condition func(T) bool) (t T, ok bool) { + if v != nil { + t, ok = slice.First(*v, condition) + } + return t, ok +} + // Sort sorts the Vector in-place and returns it func (v *Vector[T]) Sort(comparer slice.Comparer[T]) *Vector[T] { return v.sortBy(slice.Sort, comparer) diff --git a/collection/mutable/vector/api.go b/collection/mutable/vector/api.go index f33cfbc6..3bf08c93 100644 --- a/collection/mutable/vector/api.go +++ b/collection/mutable/vector/api.go @@ -4,10 +4,8 @@ package vector import ( "golang.org/x/exp/constraints" - breakLoop "github.com/m4gshm/gollections/break/loop" "github.com/m4gshm/gollections/collection" "github.com/m4gshm/gollections/collection/mutable" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" ) @@ -26,14 +24,6 @@ func NewCap[T any](capacity int) *mutable.Vector[T] { return mutable.NewVectorCap[T](capacity) } -// From instantiates a vector with elements retrieved by the 'next' function. -// The next returns an element with true or zero value with false if there are no more elements. -// -// Deprecated: replaced by [FromSeq]. -func From[T any](next func() (T, bool)) *mutable.Vector[T] { - return mutable.VectorFromLoop(next) -} - // FromSeq creates a vector with elements retrieved by the seq. func FromSeq[T any](seq seq.Seq[T]) *mutable.Vector[T] { return mutable.VectorFromSeq(seq) @@ -44,22 +34,22 @@ func Sort[T any, F constraints.Ordered](v *mutable.Vector[T], by func(T) F) *mut return collection.Sort(v, by) } -// Convert returns a loop that applies the 'converter' function to the collection elements -func Convert[From, To any](vector *mutable.Vector[From], converter func(From) To) loop.Loop[To] { +// Convert returns a seq that applies the 'converter' function to the collection elements +func Convert[From, To any](vector *mutable.Vector[From], converter func(From) To) seq.Seq[To] { return collection.Convert(vector, converter) } -// Conv returns a breakable loop that applies the 'converter' function to the collection elements -func Conv[From, To comparable](vector *mutable.Vector[From], converter func(From) (To, error)) breakLoop.Loop[To] { +// Conv returns an errorable seq that applies the 'converter' function to the collection elements +func Conv[From, To comparable](vector *mutable.Vector[From], converter func(From) (To, error)) seq.SeqE[To] { return collection.Conv(vector, converter) } -// Flat returns a loop that converts the collection elements into slices and then flattens them to one level -func Flat[From, To any](vector *mutable.Vector[From], flattener func(From) []To) loop.Loop[To] { +// Flat returns a seq that converts the collection elements into slices and then flattens them to one level +func Flat[From, To any](vector *mutable.Vector[From], flattener func(From) []To) seq.Seq[To] { return collection.Flat(vector, flattener) } -// Flatt returns a breakable loop that converts the collection elements into slices and then flattens them to one level -func Flatt[From, To comparable](vector *mutable.Vector[From], flattener func(From) ([]To, error)) breakLoop.Loop[To] { +// Flatt returns an errorable seq that converts the collection elements into slices and then flattens them to one level +func Flatt[From, To comparable](vector *mutable.Vector[From], flattener func(From) ([]To, error)) seq.SeqE[To] { return collection.Flatt(vector, flattener) } diff --git a/collection/mutable/vector/test/vector_test.go b/collection/mutable/vector/test/vector_test.go index 6c3f20d2..7cd55bc9 100644 --- a/collection/mutable/vector/test/vector_test.go +++ b/collection/mutable/vector/test/vector_test.go @@ -7,39 +7,20 @@ import ( "github.com/m4gshm/gollections/collection/mutable" "github.com/m4gshm/gollections/collection/mutable/vector" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/range_" - "github.com/m4gshm/gollections/walk/group" ) -func Test_Vector_From(t *testing.T) { - set := vector.From(loop.Of(1, 1, 2, 2, 3, 4, 3, 2, 1)) - assert.Equal(t, slice.Of(1, 1, 2, 2, 3, 4, 3, 2, 1), set.Slice()) -} - func Test_VectorIterate(t *testing.T) { expected := slice.Of(1, 2, 3, 4) v := vector.Of(1, 2, 3, 4) result := make([]int, v.Len()) i := 0 - for it := v.Head(); it.HasNext(); { - result[i] = it.GetNext() - i++ - } - assert.Equal(t, expected, result) -} - -func Test_VectorIterate2(t *testing.T) { - expected := slice.Of(1, 2, 3, 4) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - for it, v, ok := v.First(); ok; v, ok = it.Next() { - result[i] = v + for it := range v.All { + result[i] = it i++ } assert.Equal(t, expected, result) @@ -56,18 +37,6 @@ func Test_VectorIterateOverRange(t *testing.T) { assert.Equal(t, expected, result) } -func Test_VectorReverseIteration(t *testing.T) { - expected := slice.Of(4, 3, 2, 1) - v := vector.Of(1, 2, 3, 4) - result := make([]int, v.Len()) - i := 0 - for it := v.Tail(); it.HasPrev(); { - result[i] = it.GetPrev() - i++ - } - assert.Equal(t, expected, result) -} - func Test_Vector_Sort(t *testing.T) { var ( elements = vector.Of(3, 1, 5, 6, 8, 0, -2) @@ -108,32 +77,16 @@ func Test_Vector_Nil(t *testing.T) { vec.IsEmpty() vec.Len() - vec.For(nil) vec.ForEach(nil) - vec.Track(nil) vec.TrackEach(nil) assert.Equal(t, nils, vec.Slice()) - head := vec.Head() - assert.False(t, head.HasNext()) - assert.False(t, head.HasPrev()) - - _, ok := head.Get() - assert.False(t, ok) - _, ok = head.Next() + _, ok := vec.Head() assert.False(t, ok) - head.Size() - tail := vec.Tail() - assert.False(t, tail.HasNext()) - assert.False(t, tail.HasPrev()) - - _, ok = tail.Get() - assert.False(t, ok) - _, ok = tail.Next() + _, ok = vec.Tail() assert.False(t, ok) - tail.Size() } func Test_Vector_Zero(t *testing.T) { @@ -156,36 +109,18 @@ func Test_Vector_Zero(t *testing.T) { l := vec.Len() assert.Equal(t, 4, l) - vec.For(func(_ string) error { return nil }) vec.ForEach(func(_ string) {}) - vec.Track(func(_ int, _ string) error { return nil }) vec.TrackEach(func(_ int, _ string) {}) assert.Equal(t, slice.Of("a", "b", "c", "d"), vec.Slice()) - head := vec.Head() - assert.True(t, head.HasNext()) - assert.False(t, head.HasPrev()) - - _, ok := head.Get() - assert.False(t, ok) - fv, ok := head.Next() + head, ok := vec.Head() assert.True(t, ok) - assert.Equal(t, "a", fv) - c := head.Size() - assert.Equal(t, 4, c) - - tail := vec.Tail() - assert.False(t, tail.HasNext()) - assert.True(t, tail.HasPrev()) + assert.Equal(t, "a", head) - _, ok = tail.Get() - assert.False(t, ok) - tv, ok := tail.Prev() + tail, ok := vec.Tail() assert.True(t, ok) - assert.Equal(t, "d", tv) - c = tail.Size() - assert.Equal(t, 4, c) + assert.Equal(t, "d", tail) } func Test_Vector_new(t *testing.T) { @@ -208,36 +143,19 @@ func Test_Vector_new(t *testing.T) { l := vec.Len() assert.Equal(t, 4, l) - vec.For(func(_ string) error { return nil }) vec.ForEach(func(_ string) {}) - vec.Track(func(_ int, _ string) error { return nil }) vec.TrackEach(func(_ int, _ string) {}) assert.Equal(t, slice.Of("a", "b", "c", "d"), vec.Slice()) - head := vec.Head() - assert.True(t, head.HasNext()) - assert.False(t, head.HasPrev()) - - _, ok := head.Get() - assert.False(t, ok) - fv, ok := head.Next() + head, ok := vec.Head() assert.True(t, ok) - assert.Equal(t, "a", fv) - c := head.Size() - assert.Equal(t, 4, c) + assert.Equal(t, "a", head) - tail := vec.Tail() - assert.False(t, tail.HasNext()) - assert.True(t, tail.HasPrev()) - - _, ok = tail.Get() - assert.False(t, ok) - tv, ok := tail.Prev() + tail, ok := vec.Tail() assert.True(t, ok) - assert.Equal(t, "d", tv) - c = tail.Size() - assert.Equal(t, 4, c) + assert.Equal(t, "d", tail) + } func Test_Vector_AddAllOfSelf(t *testing.T) { @@ -255,20 +173,10 @@ func (u *user) Name() string { return u.name } func (u *user) Age() int { return u.age } func Test_Vector_AddAndDelete(t *testing.T) { - vec := vector.NewCap[int](0) - vec.Add(range_.Closed(0, 1000)...) - deleted := false - for i := vec.Head(); i.HasNext(); { - deleted = i.DeleteNext() - } - assert.Equal(t, deleted, true) - assert.True(t, vec.IsEmpty()) - - vec.Add(range_.Closed(0, 10000)...) - for i := vec.Tail(); i.HasPrev(); { - deleted = i.DeletePrev() - } - assert.Equal(t, deleted, true) + vec := vector.NewCap[rune](0) + vec.Add(range_.Of('a', 'a'+rune(1000))...) + assert.Equal(t, 1000, vec.Len()) + vec.Delete(range_.Of(0, 1000)...) assert.True(t, vec.IsEmpty()) } @@ -288,107 +196,6 @@ func Test_Vector_AddAll(t *testing.T) { assert.Equal(t, slice.Of(1, 1, 2, 4, 3, 1, 1), vec.Slice()) } -func Test_Vector_Add_And_Iterate(t *testing.T) { - vec := vector.NewCap[int](0) - it, v, ok := vec.First() - //no a first element - assert.False(t, ok) - //no more elements - assert.False(t, it.HasNext()) - vec.Add(1) - //exists one more - assert.True(t, it.HasNext()) - v, ok = it.Get() - //but the cursor points out of the range - assert.False(t, ok) - //starts itaration - v, ok = it.Next() - //success - assert.True(t, ok) - assert.Equal(t, 1, v) - //no more - assert.False(t, it.HasNext()) - //no prev - assert.False(t, it.HasPrev()) - //only the current one - v, ok = it.Get() - assert.True(t, ok) - assert.Equal(t, 1, v) -} - -func Test_Vector_Delete_And_Iterate(t *testing.T) { - vec := vector.Of(2) - it, v, ok := vec.First() - //success - assert.True(t, ok) - assert.Equal(t, 2, v) - - //no more - assert.False(t, it.HasNext()) - //no prev - assert.False(t, it.HasPrev()) - - //only the current one - v, ok = it.Get() - assert.True(t, ok) - assert.Equal(t, 2, v) - - it.Delete() - - //no the current one - _, ok = it.Get() - assert.False(t, ok) - - //no more - assert.False(t, it.HasNext()) - //no prev - assert.False(t, it.HasPrev()) - - assert.True(t, vec.IsEmpty()) - - // add values to vector - vec.Add(1, 3) - - //it must to point before the first - _, ok = it.Get() - assert.False(t, ok) - assert.True(t, it.HasNext()) - assert.False(t, it.HasPrev()) - - v, ok = it.Next() - assert.True(t, ok) - assert.Equal(t, 1, v) - - assert.True(t, it.HasNext()) - assert.False(t, it.HasPrev()) - - v, ok = it.Next() - assert.True(t, ok) - assert.Equal(t, 3, v) - - assert.False(t, it.HasNext()) - assert.True(t, it.HasPrev()) - - v, ok = it.Prev() - assert.True(t, ok) - assert.Equal(t, 1, v) - - assert.True(t, it.HasNext()) - assert.False(t, it.HasPrev()) - - //delete the first one - it.Delete() - - //second must remains - assert.False(t, it.HasNext()) - assert.False(t, it.HasPrev()) - v, ok = it.Get() - assert.True(t, ok) - assert.Equal(t, 3, v) - - assert.Equal(t, []int{3}, vec.Slice()) -} - func Test_Vector_DeleteOne(t *testing.T) { vec := vector.Of("1", "1", "2", "4", "3", "1") vec.DeleteOne(3) @@ -429,51 +236,7 @@ func Test_Vector_Set(t *testing.T) { assert.Equal(t, slice.Of("1", "1", "2", "4", "3", "1", "", "", "", "", "11"), vec.Slice()) } -func Test_Vector_DeleteByIterator(t *testing.T) { - vec := vector.Of(1, 1, 2, 4, 3, 1) - iterator := vec.Head() - - i := 0 - var v int - var ok bool - for v, ok = iterator.Next(); ok; v, ok = iterator.Next() { - i++ - iterator.Delete() - } - - _, _ = v, ok - - assert.Equal(t, 6, i) - assert.Equal(t, 0, len(vec.Slice())) -} - -func Test_Vector_DeleteByIterator_Reverse(t *testing.T) { - vec := vector.Of(1, 1, 2, 4, 3, 1) - iterator := vec.Tail() - - i := 0 - var v int - var ok bool - for v, ok = iterator.Prev(); ok; v, ok = iterator.Prev() { - i++ - iterator.Delete() - } - - _, _ = v, ok - - assert.Equal(t, 6, i) - assert.Equal(t, 0, len(vec.Slice())) -} - func Test_Vector_FilterMapReduce(t *testing.T) { s := vector.Of(1, 1, 2, 4, 3, 4).Filter(func(i int) bool { return i%2 == 0 }).Convert(func(i int) int { return i * 2 }).Reduce(op.Sum[int]) assert.Equal(t, 20, s) } - -func Test_Vector_Group(t *testing.T) { - groups := group.Of(vector.Of(0, 1, 1, 2, 4, 3, 1, 6, 7), func(e int) bool { return e%2 == 0 }) - - assert.Equal(t, len(groups), 2) - assert.Equal(t, []int{1, 1, 3, 1, 7}, groups[false]) - assert.Equal(t, []int{0, 2, 4, 6}, groups[true]) -} diff --git a/collection/seq.go b/collection/seq.go new file mode 100644 index 00000000..695882ea --- /dev/null +++ b/collection/seq.go @@ -0,0 +1,20 @@ +package collection + +// Seq is an iterator-function that allows to iterate over elements of a sequence, such as slice. +type Seq[T any] func(yield func(T) bool) + +// SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. +// It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. +// At each iteration step, it is necessary to check for the occurrence of an error. +// +// for e, err := range seqence { +// if err != nil { +// break +// } +// ... +// } +type SeqE[T any] func(yield func(T, error) bool) + +// Seq2 is an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. +// It is used to iterate over slice index/value pairs or map key/value pairs. +type Seq2[K, V any] func(yield func(K, V) bool) diff --git a/convert/api.go b/convert/api.go index 938590a1..444c2e22 100644 --- a/convert/api.go +++ b/convert/api.go @@ -99,21 +99,21 @@ func ExtraKeys[T, K any](element T, keysExtractor func(T) []K) (out []c.KV[K, T] return out } -// Ptr converts a value to the value pointer -func Ptr[T any](value T) *T { +// ToPtr converts a value to the value pointer +func ToPtr[T any](value T) *T { return &value } -// PtrVal returns a value referenced by the pointer or the zero value if the pointer is nil -func PtrVal[T any](pointer *T) (t T) { +// ToVal returns a value referenced by the pointer or the zero value if the pointer is nil +func ToVal[T any](pointer *T) (t T) { if pointer != nil { t = *pointer } return t } -// NoNilPtrVal returns a value referenced by the pointer or ok==false if the pointer is nil -func NoNilPtrVal[T any](pointer *T) (t T, ok bool) { +// ToValNotNil returns a value referenced by the pointer or ok==false if the pointer is nil +func ToValNotNil[T any](pointer *T) (t T, ok bool) { if pointer != nil { return *pointer, true } @@ -126,3 +126,15 @@ func ToType[T, I any](i I) (T, bool) { t, ok := a.(T) return t, ok } + +// NilSafe filters not nil elements, converts that ones, filters not nils after converting and returns them +func NilSafe[From, To any](converter func(*From) *To) func(f *From) (*To, bool) { + return func(f *From) (*To, bool) { + if f != nil { + if t := converter(f); t != nil { + return t, true + } + } + return nil, false + } +} diff --git a/convert/as/api.go b/convert/as/api.go index 43efce03..4114fd7a 100644 --- a/convert/as/api.go +++ b/convert/as/api.go @@ -17,7 +17,7 @@ func ErrTail[I, O any](f func(I) O) func(I) (O, error) { } // Ptr converts a value to the value pointer -func Ptr[T any](value T) *T { return convert.Ptr(value) } +func Ptr[T any](value T) *T { return convert.ToPtr(value) } // Val returns a value referenced by the pointer or the zero value if the pointer is nil -func Val[T any](pointer *T) T { return convert.PtrVal(pointer) } +func Val[T any](pointer *T) T { return convert.ToVal(pointer) } diff --git a/convert/ptr/api.go b/convert/ptr/api.go index 7d3b7d09..0696f5e3 100644 --- a/convert/ptr/api.go +++ b/convert/ptr/api.go @@ -5,5 +5,5 @@ import "github.com/m4gshm/gollections/convert" // Of is value-to-pointer conversion helper func Of[T any](t T) *T { - return convert.Ptr(t) + return convert.ToPtr(t) } diff --git a/convert/val/api.go b/convert/val/api.go index 82ee21ab..7bc2403c 100644 --- a/convert/val/api.go +++ b/convert/val/api.go @@ -5,5 +5,5 @@ import "github.com/m4gshm/gollections/convert" // Of is pointer-tovalue conversion helper func Of[T any](t *T) T { - return convert.PtrVal(t) + return convert.ToVal(t) } diff --git a/error_/test/error_test.go b/error_/test/error_test.go index 85420365..8accc987 100644 --- a/error_/test/error_test.go +++ b/error_/test/error_test.go @@ -1,6 +1,7 @@ package test import ( + "errors" "fmt" "testing" @@ -17,7 +18,7 @@ func (t testError) Error() string { } func Test_error_As(t *testing.T) { - e := fmt.Errorf("wrap: %w", testError("test error")) + e := errors.Join(fmt.Errorf("any error"), fmt.Errorf("wrap2: %w", fmt.Errorf("wrap: %w", testError("test error")))) err, ok := error_.As[testError](e) diff --git a/expr/use/test/use_test.go b/expr/use/test/use_test.go index 20fe15c7..52195c28 100644 --- a/expr/use/test/use_test.go +++ b/expr/use/test/use_test.go @@ -7,8 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/m4gshm/gollections/expr/use" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" ) var ( @@ -32,17 +30,6 @@ func Test_UseIfElse(t *testing.T) { assert.Equal(t, 2, result) } -func Test_UseIfOKElse(t *testing.T) { - var nilSeq loop.Loop[int] - var seq = loop.Of(1) - - result := use.IfOK(seq.ReduceOK(op.Sum)).Else(2) - assert.Equal(t, 1, result) - - result = use.IfOK(nilSeq.ReduceOK(op.Sum)).Else(2) - assert.Equal(t, 2, result) -} - func Test_UseEval(t *testing.T) { result, ok := use.If(true, 1).If(true, 2).Eval() assert.True(t, ok) diff --git a/internal/benchmark/convert/convert_benchmark_test.go b/internal/benchmark/convert/convert_benchmark_test.go index 2fde8922..2970e684 100644 --- a/internal/benchmark/convert/convert_benchmark_test.go +++ b/internal/benchmark/convert/convert_benchmark_test.go @@ -9,8 +9,6 @@ import ( "github.com/m4gshm/gollections/collection/mutable" mvector "github.com/m4gshm/gollections/collection/mutable/vector" "github.com/m4gshm/gollections/convert" - "github.com/m4gshm/gollections/convert/ptr" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/range_" @@ -61,18 +59,6 @@ func Benchmark_Convert_Seq(b *testing.B) { b.StopTimer() } -func Benchmark_Convert_Loop(b *testing.B) { - op := convert.And(toString, addTail) - var s []string - b.ResetTimer() - for i := 0; i < b.N; i++ { - it := slice.NewHead(values) - s = loop.SliceCap(loop.Convert(it.Next, op), len(values)) - } - _ = s - b.StopTimer() -} - func Benchmark_Convert_ImmutableVector_Iterable(b *testing.B) { concat := convert.And(toString, addTail) items := vector.Of(values...) @@ -123,18 +109,6 @@ func Benchmark_Convert_ImmutableVector_Append(b *testing.B) { b.StopTimer() } -func Benchmark_Convert_ImmutableVector_Head_Loop(b *testing.B) { - concat := convert.And(toString, addTail) - items := vector.Of(values...) - var s []string - b.ResetTimer() - for i := 0; i < b.N; i++ { - s = loop.SliceCap(loop.Convert(ptr.Of(items.Head()).Next, concat), len(values)) - } - _ = s - b.StopTimer() -} - func Benchmark_Convert_ImmutableVector_ForEach_To_MutableVector(b *testing.B) { concat := convert.And(toString, addTail) items := vector.Of(values...) diff --git a/internal/benchmark/loop/collection/loop_benchmark_test.go b/internal/benchmark/loop/collection/loop_benchmark_test.go index 74104b04..e5da4411 100644 --- a/internal/benchmark/loop/collection/loop_benchmark_test.go +++ b/internal/benchmark/loop/collection/loop_benchmark_test.go @@ -7,12 +7,7 @@ import ( "github.com/m4gshm/gollections/collection/immutable/set" "github.com/m4gshm/gollections/collection/immutable/vector" moset "github.com/m4gshm/gollections/collection/mutable/ordered/set" - mvector "github.com/m4gshm/gollections/collection/mutable/vector" - "github.com/m4gshm/gollections/convert/ptr" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/range_" ) @@ -62,249 +57,6 @@ func Benchmark_Loop_ImmutableOrderSet_All(b *testing.B) { } } -func Benchmark_Loop_ImmutableOrderSet_FirstNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := c.First(); ok; v, ok = it.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_HeadNextNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - h := c.Head() - for v, ok := h.Next(); ok; v, ok = h.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_LoopNextNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := c.Loop() - for v, ok := next(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_LoopCrankNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for next, v, ok := c.Loop().Crank(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_LastPrev(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := c.Last(); ok; v, ok = it.Prev() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_LoopCrankNext(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for next, v, ok := c.Loop().Crank(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_LoopNext(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := c.Loop() - for { - v, ok := next() - if !ok { - break - } - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_HeadHasNextGetNext(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it := c.Head(); it.HasNext(); { - casee.load(it.GetNext()) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_HeadNextNext(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - h := c.Head() - for v, ok := h.Next(); ok; v, ok = h.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_FirstNext(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := c.First(); ok; v, ok = it.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_TailPrevPrev(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - it := c.Tail() - for v, ok := it.Prev(); ok; v, ok = it.Prev() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_LastPrev(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := c.Last(); ok; v, ok = it.Prev() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_MutableVector_FirstNext(b *testing.B) { - c := mvector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := c.First(); ok; v, ok = it.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_MutableVector_LoopNext(b *testing.B) { - c := mvector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := c.Loop() - for v, ok := next(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableVector_TailHasPrevGetPrev(b *testing.B) { - c := vector.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it := c.Tail(); it.HasPrev(); { - casee.load(it.GetPrev()) - } - } - }) - } -} - -func Benchmark_Loop_Slice_Loop_NextNext(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Of(values...) - for v, ok := next(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_Slice_Loop_All(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - l := loop.Of(values...) - for v := range l.All { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_Slice_NewHead_HasNextGetNext(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it := slice.NewHead(values); it.HasNext(); { - casee.load(it.GetNext()) - } - } - }) - } -} - func Benchmark_Loop_Slice_Seq_ForByRange(b *testing.B) { for _, casee := range cases { b.Run(casee.name, func(b *testing.B) { @@ -373,23 +125,6 @@ func Benchmark_Loop_Map_Embedded_ForByKeyValueRange(b *testing.B) { } } -func Benchmark_Loop_Map_NewIter_NextNext(b *testing.B) { - values := map[int]int{} - for i := 0; i < max; i++ { - values[i] = i - } - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - iterator := map_.NewIter(values) - for k, _, ok := iterator.Next(); ok; k, _, ok = iterator.Next() { - casee.load(k) - } - } - }) - } -} - func Benchmark_Loop_ImmutableVector_ForEach(b *testing.B) { c := vector.Of(values...) for _, casee := range cases { @@ -449,33 +184,6 @@ func Benchmark_Loop_ImmutableOrderedSet_ForRangeSlice(b *testing.B) { } } -func Benchmark_Loop_MutableOrdererSet_FirstNext(b *testing.B) { - c := moset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for i, e, ok := c.First(); ok; e, ok = i.Next() { - casee.load(e) - } - } - }) - } -} - -func Benchmark_Loop_MutableOrdererSet_Head(b *testing.B) { - c := moset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - i := c.Head() - for e, ok := i.Next(); ok; e, ok = i.Next() { - casee.load(e) - } - } - }) - } -} - func Benchmark_Loop_MutableOrdererSet_ForEach(b *testing.B) { c := moset.Of(values...) for _, casee := range cases { @@ -486,35 +194,3 @@ func Benchmark_Loop_MutableOrdererSet_ForEach(b *testing.B) { }) } } - -func Benchmark_Loop_Loop_RangeClosed_ForEach(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - loop.RangeClosed(1, max).ForEach(casee.load) - } - }) - } -} - -func Benchmark_Loop_Loop_Of_ForEach(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - loop.Of(values...).ForEach(casee.load) - } - }) - } -} - -func Benchmark_Loop_Slice_Head_Next(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for i, v, ok := ptr.Of(slice.NewHead(values)).Crank(); ok; v, ok = i.Next() { - casee.load(v) - } - } - }) - } -} diff --git a/internal/benchmark/loop/collection2/loop_benchmark_test.go b/internal/benchmark/loop/collection2/loop_benchmark_test.go index b9c2b07f..8ff2bf48 100644 --- a/internal/benchmark/loop/collection2/loop_benchmark_test.go +++ b/internal/benchmark/loop/collection2/loop_benchmark_test.go @@ -4,7 +4,6 @@ import ( "testing" oset "github.com/m4gshm/gollections/collection/immutable/ordered/set" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice/range_" ) @@ -66,81 +65,6 @@ func Benchmark_Loop_Slice_Embedded_ForByRange(b *testing.B) { } } -func Benchmark_Loop_ImmutableOrderSet_FirstNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - for it, v, ok := c.First(); ok; v, ok = it.Next() { - casee.load(v) - } - } - b.StopTimer() - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_FirstNext2(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - it, v, ok := c.First() - for ok { - casee.load(v) - v, ok = it.Next() - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_Head_HasNext_GetNext(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - it := c.Head() - for it.HasNext() { - casee.load(it.GetNext()) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_Loop_Crank_Next(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for next, v, ok := c.Loop().Crank(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Loop_ImmutableOrderSet_Loop_Next(b *testing.B) { - c := oset.Of(values...) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := c.Loop() - for { - v, ok := next() - if !ok { - break - } - casee.load(v) - } - } - }) - } -} - func Benchmark_Loop_ImmutableOrderSet_ForRange_All(b *testing.B) { c := oset.Of(values...) for _, casee := range cases { @@ -153,16 +77,3 @@ func Benchmark_Loop_ImmutableOrderSet_ForRange_All(b *testing.B) { }) } } - -func Benchmark_Loop_Slice_Loop_NextNext(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Of(values...) - for v, ok := next(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} diff --git a/internal/benchmark/loop/loop/int_range_go_1_22_test.go b/internal/benchmark/loop/loop/int_range_go_1_22_test.go index e9f28ecf..c7624ddf 100644 --- a/internal/benchmark/loop/loop/int_range_go_1_22_test.go +++ b/internal/benchmark/loop/loop/int_range_go_1_22_test.go @@ -1,5 +1,3 @@ -//go:build goexperiment.rangefunc - package loop import "testing" diff --git a/internal/benchmark/loop/loop/loop_range_test.go b/internal/benchmark/loop/loop/loop_range_test.go index b744d842..1ed728a7 100644 --- a/internal/benchmark/loop/loop/loop_range_test.go +++ b/internal/benchmark/loop/loop/loop_range_test.go @@ -3,9 +3,6 @@ package loop import ( "testing" - "github.com/m4gshm/gollections/collection/mutable" - "github.com/m4gshm/gollections/convert/ptr" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" ) @@ -57,89 +54,3 @@ func Benchmark_SeqRange_Iterating(b *testing.B) { }) } } - -func Benchmark_LoopRange_Iterating(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Range(0, max) - for v, ok := next(); ok; v, ok = next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_LoopRange_Iterating2(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Range(0, max) - v, ok := next() - for ok { - casee.load(v) - v, ok = next() - } - } - }) - } -} - -func Benchmark_LoopRange_Iterating3(b *testing.B) { - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Range(0, max) - for v := range next.All { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Slice_Iter_Iterating(b *testing.B) { - integers := slice.Range(0, max) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - for it, v, ok := ptr.Of(slice.NewHead(integers)).Crank(); ok; v, ok = it.Next() { - casee.load(v) - } - } - }) - } -} - -func Benchmark_Slice_Iter_Iterating2(b *testing.B) { - integers := slice.Range(0, max) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - it := slice.NewHead(integers) - v, ok := it.Next() - for ok { - casee.load(v) - v, ok = it.Next() - } - } - }) - } -} - -func Benchmark_Slice_Mutable_Iter_Iterating(b *testing.B) { - integers := slice.Range(0, max) - for _, casee := range cases { - b.Run(casee.name, func(b *testing.B) { - for i := 0; i < b.N; i++ { - it := mutable.NewHead(&integers, nil) - v, ok := it.Next() - for ok { - casee.load(v) - v, ok = it.Next() - } - } - }) - } -} diff --git a/internal/benchmark/loop/over_vs_loop/over_test.go b/internal/benchmark/loop/over_vs_loop/over_test.go deleted file mode 100644 index 672c75ae..00000000 --- a/internal/benchmark/loop/over_vs_loop/over_test.go +++ /dev/null @@ -1,84 +0,0 @@ -//go:build goexperiment.rangefunc - -package over_vs_loop - -import ( - "strconv" - "testing" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/range_" - "github.com/m4gshm/gollections/seq" -) - -var max = 100000 - -var resultStr = "" - -func Benchmark_loop_Converted(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - loop.Convert(integers, strconv.Itoa).ForEach(func(element string) { - resultStr = element - }) - } -} - -func Benchmark_loop_Converted_All(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - for element := range loop.Convert(integers, strconv.Itoa).All { - resultStr = element - } - } -} - -func Benchmark_over_Converted(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - for element := range seq.Convert(integers.All, strconv.Itoa) { - resultStr = element - } - } -} - -func Benchmark_over_Converted_direct(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - seq.Convert(integers.All, strconv.Itoa)(func(element string) bool { - resultStr = element - return true - }) - } -} - -func even(i int) bool { - return i%2 == 0 -} - -func Benchmark_loop_Convert_Filtered(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - loop.Convert(loop.Filter(integers, even), strconv.Itoa).ForEach(func(element string) { - resultStr = element - }) - } -} - -func Benchmark_loop_Convert_Filtered_rangefunc(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - for element := range loop.Convert(loop.Filter(integers, even), strconv.Itoa).All { - resultStr = element - } - } -} - -func Benchmark_over_Convert_Filtered(b *testing.B) { - integers := range_.Of(0, max) - for i := 0; i < b.N; i++ { - for element := range seq.Convert(seq.Filter(integers.All, even), strconv.Itoa) { - resultStr = element - } - } -} diff --git a/internal/benchmark/mapreduce/map_flat_fit_benchmark_test.go b/internal/benchmark/mapreduce/map_flat_fit_benchmark_test.go index 7a7ef855..f69171ed 100644 --- a/internal/benchmark/mapreduce/map_flat_fit_benchmark_test.go +++ b/internal/benchmark/mapreduce/map_flat_fit_benchmark_test.go @@ -2,15 +2,12 @@ package mapreduce import ( "fmt" - "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/m4gshm/gollections/convert" "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/convert/ptr" - "github.com/m4gshm/gollections/loop" sop "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/op/check/not" "github.com/m4gshm/gollections/seq" @@ -27,26 +24,6 @@ var ( threshhold = max / 2 ) -func Benchmark_Head_Slice(b *testing.B) { - var f int - b.ResetTimer() - for i := 0; i < b.N; i++ { - f, _ = slice.Head(values) - } - b.StopTimer() - assert.Equal(b, 1, f) -} - -func Benchmark_Tail_Slice(b *testing.B) { - var f int - b.ResetTimer() - for i := 0; i < b.N; i++ { - f, _ = slice.Tail(values) - } - b.StopTimer() - assert.Equal(b, max, f) -} - func Benchmark_First_PlainOld(b *testing.B) { op := func(i int) bool { return i > threshhold } var f int @@ -109,22 +86,6 @@ func Benchmark_LastI_Slice(b *testing.B) { assert.Equal(b, threshhold-2, ind) } -func Benchmark_ConvertAndFilter_Slice_Loop(b *testing.B) { - var ( - toString = func(i int) string { return fmt.Sprintf("%d", i) } - addTail = func(s string) string { return s + "_tail" } - even = func(v int) bool { return v%2 == 0 } - ) - items := slice.Of(1, 2, 3, 4, 5) - var s []string - b.ResetTimer() - for i := 0; i < b.N; i++ { - s = loop.Slice(loop.Convert(loop.Filter(ptr.Of(slice.NewHead(items)).Next, even), convert.And(toString, addTail))) - } - _ = s - b.StopTimer() -} - func Benchmark_ConvertAndFilter_Slice_Seq(b *testing.B) { var ( toString = func(i int) string { return fmt.Sprintf("%d", i) } @@ -163,24 +124,6 @@ func Benchmark_ConvertAndFilter_Slice_PlainOld(b *testing.B) { b.StopTimer() } -func Benchmark_FilterAndConvert_Loop(b *testing.B) { - var ( - toString = func(i int) string { return fmt.Sprintf("%d", i) } - addTail = func(s string) string { return s + "_tail" } - even = func(v int) bool { return v%2 == 0 } - ) - items := slice.Of(1, 2, 3, 4, 5) - var s []string - b.ResetTimer() - for i := 0; i < b.N; i++ { - next := loop.Of(items...) - s = loop.Slice(loop.FilterAndConvert(next, even, convert.And(toString, addTail))) - } - _ = s - - b.StopTimer() -} - func Benchmark_FilterAndConvert_Embedder_Slice(b *testing.B) { var ( toString = func(i int) string { return fmt.Sprintf("%d", i) } @@ -198,18 +141,6 @@ func Benchmark_FilterAndConvert_Embedder_Slice(b *testing.B) { b.StopTimer() } -func Benchmark_Flatt_Loop(b *testing.B) { - odds := func(v int) bool { return v%2 != 0 } - multiDimension := [][][]int{{{1, 2, 3}, {4, 5, 6}}, {{7}, nil}, nil} - b.ResetTimer() - for i := 0; i < b.N; i++ { - next := loop.Of(multiDimension...) - oneDimension := loop.Slice(loop.Filter(loop.Flat(loop.Flat(next, as.Is), as.Is), odds)) - _ = oneDimension - } - b.StopTimer() -} - func Benchmark_Flatt_Seq(b *testing.B) { odds := func(v int) bool { return v%2 != 0 } multiDimension := [][][]int{{{1, 2, 3}, {4, 5, 6}}, {{7}, nil}, nil} @@ -243,21 +174,6 @@ func Benchmark_Flatt_Slice_PlainOld(b *testing.B) { //go:noinline func odds(v int) bool { return v%2 != 0 } -func Benchmark_ReduceSum_Loop(b *testing.B) { - odds := func(v int) bool { return v%2 != 0 } - multiDimension := [][][]int{{{1, 2, 3}, {4, 5, 6}}, {{7}, nil}, nil} - expected := 1 + 3 + 5 + 7 - b.ResetTimer() - result := 0 - for i := 0; i < b.N; i++ { - result = loop.Reduce(loop.Filter(loop.Flat(loop.Flat(loop.Of(multiDimension...), as.Is), as.Is), odds), sop.Sum) - } - b.StopTimer() - if result != expected { - b.Fatalf("must be %d, but %d", expected, result) - } -} - func Benchmark_ReduceSum_Seq(b *testing.B) { odds := func(v int) bool { return v%2 != 0 } multiDimension := [][][]int{{{1, 2, 3}, {4, 5, 6}}, {{7}, nil}, nil} @@ -265,7 +181,7 @@ func Benchmark_ReduceSum_Seq(b *testing.B) { b.ResetTimer() result := 0 for i := 0; i < b.N; i++ { - result = seq.Reduce(seq.Filter(seq.Flat(seq.Flat(seq.Of(multiDimension...), as.Is), as.Is), odds), sop.Sum) + result = seq.Flat(seq.Flat(seq.Of(multiDimension...), as.Is), as.Is).Filter(odds).Reduce(sop.Sum) } b.StopTimer() if result != expected { @@ -353,48 +269,6 @@ func (p *Participant) GetAttributes() []*Attributes { return p.attributes } -func Benchmark_ConvertFlattStructure_LoopNotNil(b *testing.B) { - items := []*Participant{{attributes: []*Attributes{{name: "first"}, {name: "second"}, nil}}, nil} - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = loop.Slice(loop.Convert(loop.NotNil[Attributes](loop.Flat(loop.NotNil[Participant](loop.Of(items...)), (*Participant).GetAttributes)), (*Attributes).GetName)) - } - b.StopTimer() -} - -func Benchmark_ConvertFlattStructure_LoopWithoutNotNilFiltering(b *testing.B) { - items := []*Participant{{attributes: []*Attributes{{name: "first"}, {name: "second"}, nil}}, nil} - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = loop.Slice(loop.Convert(loop.Flat(loop.Of(items...), (*Participant).GetAttributes), (*Attributes).GetName)) - } - b.StopTimer() -} - -func Benchmark_ConvertFlattStructure_IterableFilt(b *testing.B) { - items := []*Participant{{attributes: []*Attributes{{name: "first"}, {name: "second"}, nil}}, nil} - expected := []string{"first", "second"} - result := []string{} - b.ResetTimer() - for i := 0; i < b.N; i++ { - result = loop.Slice(loop.FilterAndConvert(loop.FilterAndFlat(loop.Of(items...), not.Nil, (*Participant).GetAttributes), not.Nil, (*Attributes).GetName)) - } - if !reflect.DeepEqual(expected, result) { - b.Fatalf("must be %v, but %v", expected, result) - } - b.StopTimer() -} - -func Benchmark_ConvertFlattStructure_Loop(b *testing.B) { - items := []*Participant{{attributes: []*Attributes{{name: "first"}, {name: "second"}, nil}}, nil} - b.ResetTimer() - for i := 0; i < b.N; i++ { - attr := loop.FilterAndFlat(loop.Of(items...), not.Nil, (*Participant).GetAttributes) - _ = loop.Slice(loop.FilterAndConvert(attr, not.Nil, (*Attributes).GetName)) - } - b.StopTimer() -} - func Benchmark_ConvertFlattStructure_Seq(b *testing.B) { items := []*Participant{{attributes: []*Attributes{{name: "first"}, {name: "second"}, nil}}, nil} b.ResetTimer() diff --git a/internal/benchmark/range_/range_benchmark_test.go b/internal/benchmark/range_/range_benchmark_test.go index 5c57eadb..cd2cae6b 100644 --- a/internal/benchmark/range_/range_benchmark_test.go +++ b/internal/benchmark/range_/range_benchmark_test.go @@ -3,8 +3,6 @@ package range_ import ( "testing" - "github.com/m4gshm/gollections/loop" - lrange "github.com/m4gshm/gollections/loop/range_" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice" srange "github.com/m4gshm/gollections/slice/range_" @@ -37,19 +35,6 @@ func Benchmark_Seq_RangeClosed_Iterate(b *testing.B) { } } -func Benchmark_Loop_RangeClosed_GenerateIterate(b *testing.B) { - for i := 0; i < b.N; i++ { - next := lrange.Closed(1, maxVal) - for { - n, ok := next() - if !ok { - break - } - _ = n - } - } -} - func Benchmark_Slice_Series_Generate(b *testing.B) { for i := 0; i < b.N; i++ { for n := range slice.Series(1, func(prev int) (int, bool) { return prev + 1, prev <= maxVal }) { @@ -65,16 +50,3 @@ func Benchmark_Seq_Series_Generate(b *testing.B) { } } } - -func Benchmark_Loop_Series_Generate_Iterate(b *testing.B) { - for i := 0; i < b.N; i++ { - next := loop.Series(1, func(prev int) (int, bool) { return prev + 1, prev <= maxVal }) - for { - n, ok := next() - if !ok { - break - } - _ = n - } - } -} diff --git a/internal/docs/readme.adoc b/internal/docs/readme.adoc index 35f97c55..da29f6ca 100644 --- a/internal/docs/readme.adoc +++ b/internal/docs/readme.adoc @@ -306,11 +306,6 @@ or include::../examples/seqexamples/seq2_filter_convert_reduce_test.go[lines=14..17,indent=0] ---- ==== Sequence API - -To use any collection or loop as a rangefunc sequecne just call link:#iterating-over-collections[All] method of that one. - -In many cases the API likes the link:#loop-kvloop-and-breakable-versions-breakloop-breakkvloop[loop] API. - ===== Instantiators ====== seq.Of, seq2.Of, seq2.OfMap [source,go] @@ -330,7 +325,7 @@ import( "github.com/m4gshm/gollections/seq2" ) -include::../examples/seqexamples/seq_OfNextPush_test.go[lines=13..18,indent=0] +include::../examples/seqexamples/seq_OfNextPush_test.go[lines=13..19,indent=0] ---- instead of: [source,go] @@ -339,7 +334,7 @@ import( "database/sql" "log" ) -include::../examples/seqexamples/seq_OfNextPush_test.go[lines=25..37,indent=0] +include::../examples/seqexamples/seq_OfNextPush_test.go[lines=26..38,indent=0] ---- ====== seq.Range, seq2.Range [source,go] @@ -355,13 +350,14 @@ include::../examples/seqexamples/seq_Range_test.go[lines=12..18,indent=0] import( "github.com/m4gshm/gollections/seq" ) -include::../examples/seqexamples/seq2_Series_test.go[lines=12..20,indent=0] +include::../examples/seqexamples/seq2_Series_test.go[lines=12..23,indent=0] ---- ===== Collectors ====== seq.Slice [source,go] ---- -include::../examples/seqexamples/seq_Slice_test.go[lines=12..16,indent=0] +include::../examples/seqexamples/seq_Slice_test.go[lines=12..18,indent=0] +include::../examples/seqexamples/seq_Slice_test.go[lines=20..24,indent=0] ---- ====== seq.Group, seq2.Group, seq2.Map [source,go] @@ -381,12 +377,14 @@ include::../examples/seqexamples/seq2_Group_test.go[lines=17..25,indent=0] ====== seq.Reduce [source,go] ---- -include::../examples/seqexamples/seq_Reduce_test.go[lines=12..15,indent=0] +include::../examples/seqexamples/seq_Reduce_test.go[lines=12..16,indent=0] +include::../examples/seqexamples/seq_Reduce_test.go[lines=18..22,indent=0] ---- ====== seq.ReduceOK [source,go] ---- -include::../examples/seqexamples/seq_ReduceOK_test.go[lines=12..21,indent=0] +include::../examples/seqexamples/seq_ReduceOK_test.go[lines=12..16,indent=0] +include::../examples/seqexamples/seq_ReduceOK_test.go[lines=19..24,indent=0] ---- ====== seq.First [source,go] @@ -396,6 +394,7 @@ import ( "github.com/m4gshm/gollections/seq" ) include::../examples/seqexamples/seq_First_test.go[lines=13..15,indent=0] +include::../examples/seqexamples/seq_First_test.go[lines=18..21,indent=0] ---- ====== seq.Head [source,go] @@ -404,6 +403,7 @@ import ( "github.com/m4gshm/gollections/seq" ) include::../examples/seqexamples/seq_Head_test.go[lines=12..14,indent=0] +include::../examples/seqexamples/seq_Head_test.go[lines=17..20,indent=0] ---- ===== Element converters ====== seq.Convert @@ -442,6 +442,7 @@ import ( "github.com/m4gshm/gollections/seq" ) include::../examples/seqexamples/seq_Top_test.go[lines=12..15,indent=0] +include::../examples/seqexamples/seq_Top_test.go[lines=17..21,indent=0] ---- ====== seq.Skip [source,go] @@ -450,6 +451,7 @@ import ( "github.com/m4gshm/gollections/seq" ) include::../examples/seqexamples/seq_Skip_test.go[lines=12..15,indent=0] +include::../examples/seqexamples/seq_Skip_test.go[lines=17..21,indent=0] ---- ====== seq.Flat, seq.FlatSeq, seqe.Flat, seqe.FlatSeq [source,go] @@ -458,194 +460,7 @@ import ( "github.com/m4gshm/gollections/convert/as" "github.com/m4gshm/gollections/seq" ) -include::../examples/seqexamples/seq_Flat_test.go[lines=13..16,indent=0] ----- - -[#loop] -=== link:./loop/api.go[loop], link:./kv/loop/api.go[kv/loop] and breakable versions link:./break/loop/api.go[break/loop], link:./break/kv/loop/api.go[break/kv/loop] - -*Deprecated*: will be replaced by link:#seq-seq2-seqe[seq] API. - -Legacy iterators API based on the following functions: - -[source,go] ----- -include::../examples/loopexamples/type.go[lines=2..9,indent=0] ----- - -The `Loop` function returns a next element from a dataset and returns ``ok==true`` on success. ``ok==false`` means there are no more elements in the dataset. + -The `KVLoop` behaves similar but returns key/value pairs. + - -[source,go] ----- -include::../examples/loopexamples/loop_filter_convert_reduce_test.go[lines=13..17,indent=0] ----- - -`BreakLoop` and `BreakKVLoop` are used for sources that can issue an error. - -[source,go] ----- -include::../examples/loopexamples/breakloop/loop_filter_convert_reduce_test.go[lines=13..16,indent=0] ----- - -The API in most cases is similar to the link:./slice/api.go[slice] API but with delayed computation which means that the methods don't compute a result but only return a loop provider. The loop provider is type with a ``Next`` method that returns a next processed element. - -==== Main loop functions -===== Instantiators -====== loop.Of, loop.S -[source,go] ----- -import "github.com/m4gshm/gollections/loop" -include::../examples/loopexamples/loop_Of_loop_S_test.go[lines=12..17,indent=0] ----- -====== range_.Of -[source,go] ----- -import "github.com/m4gshm/gollections/loop/range_" -include::../examples/loopexamples/range_Of_test.go[lines=11..15,indent=0] ----- -====== range_.Closed -[source,go] ----- -include::../examples/loopexamples/range_Closed_test.go[lines=12..16,indent=0] ----- -===== Collectors -====== loop.Slice -[source,go] ----- -include::../examples/loopexamples/loop_Slice_test.go[lines=12..16,indent=0] ----- -====== group.Of -[source,go] ----- -import ( - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/expr/use" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/group" -) -include::../examples/loopexamples/group_Of_test.go[lines=17..23,indent=0] ----- -====== loop.Map, loop.MapResolv -[source,go] ----- -import ( - "github.com/m4gshm/gollections/map_/resolv" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/loop" -) -include::../examples/loopexamples/loop_ToMapResolv_test.go[lines=14..22,indent=0] ----- -===== Reducers -====== sum.Of -[source,go] ----- -import ( - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/sum" -) -include::../examples/loopexamples/sum_Of_test.go[lines=13..15,indent=0] ----- -====== loop.Reduce -[source,go] ----- -include::../examples/loopexamples/loop_Reduce_test.go[lines=12..15,indent=0] ----- -====== loop.ReduceOK -[source,go] ----- -include::../examples/loopexamples/loop_ReduceOK_test.go[lines=12..21,indent=0] ----- -====== loop.Accum -[source,go] ----- -import ( - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" -) -include::../examples/loopexamples/loop_Accum_test.go[lines=13..16,indent=0] ----- -====== loop.First -[source,go] ----- -import ( - "github.com/m4gshm/gollections/predicate/more" - "github.com/m4gshm/gollections/loop" -) -include::../examples/loopexamples/loop_First_test.go[lines=13..15,indent=0] ----- -===== Element converters -====== loop.Convert -[source,go] ----- -include::../examples/loopexamples/loop_Convert_test.go[lines=12..15,indent=0] ----- -====== loop.Conv -[source,go] ----- -include::../examples/loopexamples/loop_Conv_test.go[lines=13..16,indent=0] ----- -===== Loop converters -====== loop.Filter -[source,go] ----- -import ( - "github.com/m4gshm/gollections/predicate/exclude" - "github.com/m4gshm/gollections/predicate/one" - "github.com/m4gshm/gollections/loop" -) -include::../examples/loopexamples/loop_Filter.go[lines=14..20,indent=0] ----- -====== loop.Flat -[source,go] ----- -import ( - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" -) -include::../examples/loopexamples/loop_Flat_test.go[lines=13..16,indent=0] ----- -===== Operations chain functions -* convert.AndReduce, conv.AndReduce -* convert.AndFilter -* filter.AndConvert - -These functions combine converters, filters and reducers. - -==== Iterating over loops -* Using rangefunc `All` like: - -[source,go] ----- -include::../examples/loopexamples/loop_iterating_go_1_22_test.go[lines=12..16,indent=0] ----- - -* Using `for` statement like: - -[source,go] ----- -include::../examples/loopexamples/loop_iterating_test.go[lines=11..16,indent=0] ----- - -* or - -[source,go] ----- -include::../examples/loopexamples/loop_iterating_test.go[lines=20..24,indent=0] ----- - -* `ForEach` method - -[source,go] ----- -include::../examples/loopexamples/loop_iterating_test.go[lines=28..30,indent=0] ----- - -* or `For` method that can be aborted by returning `Break` for expected completion, or another error otherwise. - -[source,go] ----- -include::../examples/loopexamples/loop_iterating_test.go[lines=34..42,indent=0] +include::../examples/seqexamples/seq_Flat_test.go[lines=13..17,indent=0] ---- === Data structures @@ -765,12 +580,5 @@ include::../examples/collection/collection_iterating_go_1_22_test.go[lines=13..1 [source,go] ---- -include::../examples/collection/collection_iterating_test.go[lines=31..34,indent=0] ----- - -* or `For` method that can be aborted by returning `Break` for expected completion, or another error otherwise. - -[source,go] ----- -include::../examples/collection/collection_iterating_test.go[lines=38..47,indent=0] +include::../examples/collection/collection_iterating_test.go[lines=10..13,indent=0] ---- diff --git a/internal/examples/boilerplate/loop_shortcuts_test.go b/internal/examples/boilerplate/loop_shortcuts_test.go deleted file mode 100644 index 10a7b6e0..00000000 --- a/internal/examples/boilerplate/loop_shortcuts_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package boilerplate - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/group" - "github.com/m4gshm/gollections/slice" -) - -func Test_Loop_schortcuts(t *testing.T) { - - data := loop.Of("Bob", "Chris", "Alice") - - lowers := loop.Convert(data, strings.ToLower) - - var lengthMap map[int][]string = group.Of(lowers, func(name string) int { return len(name) }, as.Is[string]) // converting to map - - assert.Equal(t, slice.Of("chris", "alice"), lengthMap[5]) - -} diff --git a/internal/examples/collection/collection_iterating_go_1_22_test.go b/internal/examples/collection/collection_iterating_go_1_22_test.go index 6bc63925..980f58d8 100644 --- a/internal/examples/collection/collection_iterating_go_1_22_test.go +++ b/internal/examples/collection/collection_iterating_go_1_22_test.go @@ -6,12 +6,11 @@ import ( "testing" "github.com/m4gshm/gollections/collection/immutable/set" - "github.com/m4gshm/gollections/loop/range_" ) func Test_Iterating_Rangefunc(t *testing.T) { - uniques := set.From(range_.Of(0, 100)) + uniques := set.Of(1, 2, 3, 4, 5, 6) for i := range uniques.All { doOp(i) } diff --git a/internal/examples/collection/collection_iterating_test.go b/internal/examples/collection/collection_iterating_test.go index 30dcae42..bad1ff3b 100644 --- a/internal/examples/collection/collection_iterating_test.go +++ b/internal/examples/collection/collection_iterating_test.go @@ -4,49 +4,15 @@ import ( "testing" "github.com/m4gshm/gollections/collection/immutable/set" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/range_" ) -func Test_Iterating_Loop(t *testing.T) { - - uniques := set.From(range_.Of(0, 100)) - next := uniques.Loop() - for i, ok := next(); ok; i, ok = next() { - doOp(i) - } - -} - -func Test_Iterating_Iter(t *testing.T) { - - uniques := set.From(range_.Of(0, 100)) - for iter, i, ok := uniques.First(); ok; i, ok = iter.Next() { - doOp(i) - } - -} - func Test_Iterating_ForEach(t *testing.T) { - uniques := set.From(range_.Of(0, 100)) + uniques := set.Of(1, 2, 3, 4, 5, 6) uniques.ForEach(doOp) } -func Test_Iterating_For(t *testing.T) { - - uniques := set.From(range_.Of(0, 100)) - uniques.For(func(i int) error { - if i > 22 { - return loop.Break - } - doOp(i) - return loop.Continue - }) - -} - func doOp(i int) { } diff --git a/internal/examples/collection/functions/collection_functions_test.go b/internal/examples/collection/functions/collection_functions_test.go index 106acf53..64b73504 100644 --- a/internal/examples/collection/functions/collection_functions_test.go +++ b/internal/examples/collection/functions/collection_functions_test.go @@ -7,26 +7,21 @@ import ( "github.com/m4gshm/gollections/collection/immutable/ordered/set" "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/kv/loop/group" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/predicate/more" + "github.com/m4gshm/gollections/seq" + "github.com/m4gshm/gollections/seq2" ) func Test_group_orderset_with_filtering_by_string_len(t *testing.T) { - var groupedByLength = group.Of(loop.KeyValue(set.Of( - "seventh", "seventh", //duplicate + var groupedByLength = seq2.Group(seq.ToKV(set.Of( + "seventh", "seventh", "first", "second", "third", "fourth", "fifth", "sixth", "eighth", "ninth", "tenth", "one", "two", "three", "1", - ).Loop(), func(v string) int { return len(v) }, as.Is, - ).FilterKey( - more.Than(3), - ).ConvertValue( - func(v string) string { return v + "_" }, - )) + ).All, func(v string) int { return len(v) }, as.Is, + ).FilterKey(more.Than(3)).ConvertValue(func(v string) string { return v + "_" })) assert.Equal(t, []string{"first_", "third_", "fifth_", "sixth_", "ninth_", "tenth_", "three_"}, groupedByLength[5]) assert.Equal(t, []string{"second_", "fourth_", "eighth_"}, groupedByLength[6]) assert.Equal(t, []string{"seventh_"}, groupedByLength[7]) - } diff --git a/internal/examples/loopexamples/breakloop/loop_filter_convert_reduce_test.go b/internal/examples/loopexamples/breakloop/loop_filter_convert_reduce_test.go deleted file mode 100644 index 31dea91a..00000000 --- a/internal/examples/loopexamples/breakloop/loop_filter_convert_reduce_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package breakableloop - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/break/loop" -) - -func Test_Usage_Loop(t *testing.T) { - - intSeq := loop.Conv(loop.Of("1", "2", "3", "ddd4", "5"), strconv.Atoi) - ints, err := loop.Slice(intSeq) //[1 2 3], invalid syntax - - assert.Equal(t, []int{1, 2, 3}, ints) - assert.ErrorContains(t, err, "invalid syntax") - -} diff --git a/internal/examples/loopexamples/data_users_test.go b/internal/examples/loopexamples/data_users_test.go deleted file mode 100644 index db2fd5a8..00000000 --- a/internal/examples/loopexamples/data_users_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package loopexamples - -// see the User structure above -var users = []User{ - {name: "Bob", age: 26}, - {name: "Alice", age: 35}, - {name: "Tom", age: 18}, - {name: "Chris", age: 41}, -} diff --git a/internal/examples/loopexamples/group_Of_test.go b/internal/examples/loopexamples/group_Of_test.go deleted file mode 100644 index 1ecd859c..00000000 --- a/internal/examples/loopexamples/group_Of_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/expr/use" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/group" - "github.com/m4gshm/gollections/slice" - "github.com/m4gshm/gollections/slice/sort" -) - -func Test_Group(t *testing.T) { - - var ageGroups map[string][]User = group.Of(loop.Of(users...), func(u User) string { - return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30") - }, as.Is) - - //map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]] - - assert.Equal(t, slice.Of("Alice", "Chris"), sort.Asc(slice.Convert(ageGroups[">30"], User.Name))) - assert.Equal(t, slice.Of("Bob"), slice.Convert(ageGroups["<=30"], User.Name)) - assert.Equal(t, slice.Of("Tom"), slice.Convert(ageGroups["<=20"], User.Name)) -} diff --git a/internal/examples/loopexamples/loop_Accum_test.go b/internal/examples/loopexamples/loop_Accum_test.go deleted file mode 100644 index 31a71308..00000000 --- a/internal/examples/loopexamples/loop_Accum_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" -) - -func Test_Loop_Accum(t *testing.T) { - - var sum = loop.Accum(100, loop.Of(1, 2, 3, 4, 5, 6), op.Sum) - //121 - - assert.Equal(t, 121, sum) -} diff --git a/internal/examples/loopexamples/loop_Conv_test.go b/internal/examples/loopexamples/loop_Conv_test.go deleted file mode 100644 index a0f55303..00000000 --- a/internal/examples/loopexamples/loop_Conv_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package loopexamples - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_Conv(t *testing.T) { - - result, err := loop.Conv(loop.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi).Slice() - //[]int{1, 3, 5}, ErrSyntax - - assert.Equal(t, []int{1, 3, 5}, result) - assert.ErrorIs(t, err, strconv.ErrSyntax) -} diff --git a/internal/examples/loopexamples/loop_Convert_test.go b/internal/examples/loopexamples/loop_Convert_test.go deleted file mode 100644 index 6e154fdc..00000000 --- a/internal/examples/loopexamples/loop_Convert_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package loopexamples - -import ( - "strconv" - "testing" - - "github.com/m4gshm/gollections/loop" - "github.com/stretchr/testify/assert" -) - -func Test_Convert(t *testing.T) { - - var s []string = loop.Convert(loop.Of(1, 3, 5, 7, 9, 11), strconv.Itoa).Slice() - //[]string{"1", "3", "5", "7", "9", "11"} - - assert.Equal(t, []string{"1", "3", "5", "7", "9", "11"}, s) -} diff --git a/internal/examples/loopexamples/loop_Filter.go b/internal/examples/loopexamples/loop_Filter.go deleted file mode 100644 index 4f1a9f73..00000000 --- a/internal/examples/loopexamples/loop_Filter.go +++ /dev/null @@ -1,23 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/predicate/exclude" - "github.com/m4gshm/gollections/predicate/one" -) - -func Test_OneOf(t *testing.T) { - - var f1 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11))).Slice() - //[]int{1, 7, 11} - - var f2 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), exclude.All(1, 7, 11)).Slice() - //[]int{3, 5, 9} - - assert.Equal(t, []int{1, 7, 11}, f1) - assert.Equal(t, []int{3, 5, 9}, f2) -} diff --git a/internal/examples/loopexamples/loop_First_test.go b/internal/examples/loopexamples/loop_First_test.go deleted file mode 100644 index 7be9af67..00000000 --- a/internal/examples/loopexamples/loop_First_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/predicate/more" -) - -func Test_First(t *testing.T) { - - result, ok := loop.First(loop.Of(1, 3, 5, 7, 9, 11), more.Than(5)) //7, true - - assert.True(t, ok) - assert.Equal(t, 7, result) -} diff --git a/internal/examples/loopexamples/loop_Flat_test.go b/internal/examples/loopexamples/loop_Flat_test.go deleted file mode 100644 index bb303f90..00000000 --- a/internal/examples/loopexamples/loop_Flat_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" -) - -func Test_Flat(t *testing.T) { - - var i []int = loop.Flat(loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), as.Is).Slice() - //[]int{1, 2, 3, 4, 5, 6} - - assert.Equal(t, []int{1, 2, 3, 4, 5, 6}, i) -} diff --git a/internal/examples/loopexamples/loop_Of_loop_S_test.go b/internal/examples/loopexamples/loop_Of_loop_S_test.go deleted file mode 100644 index 17b59fb2..00000000 --- a/internal/examples/loopexamples/loop_Of_loop_S_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_LoopOf_LoopS(t *testing.T) { - - var ( - ints = loop.Of(1, 2, 3) - strings = loop.S([]string{"a", "b", "c"}) - ) - - assert.Equal(t, []int{1, 2, 3}, ints.Slice()) - assert.Equal(t, []string{"a", "b", "c"}, strings.Slice()) -} diff --git a/internal/examples/loopexamples/loop_ReduceOK_test.go b/internal/examples/loopexamples/loop_ReduceOK_test.go deleted file mode 100644 index 8d1c4969..00000000 --- a/internal/examples/loopexamples/loop_ReduceOK_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_Loop_ReduceOKSum(t *testing.T) { - - adder := func(i1, i2 int) int { return i1 + i2 } - - sum, ok := loop.ReduceOK(loop.Of(1, 2, 3, 4, 5, 6), adder) - //21, true - - emptyLoop := loop.Of[int]() - sum, ok = loop.ReduceOK(emptyLoop, adder) - //0, false - - assert.False(t, ok) - assert.Equal(t, 0, sum) -} diff --git a/internal/examples/loopexamples/loop_Reduce_test.go b/internal/examples/loopexamples/loop_Reduce_test.go deleted file mode 100644 index 03487cf5..00000000 --- a/internal/examples/loopexamples/loop_Reduce_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_Loop_ReduceSum(t *testing.T) { - - var sum = loop.Reduce(loop.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 + i2 }) - //21 - - assert.Equal(t, 21, sum) -} diff --git a/internal/examples/loopexamples/loop_Slice_test.go b/internal/examples/loopexamples/loop_Slice_test.go deleted file mode 100644 index 9bcde661..00000000 --- a/internal/examples/loopexamples/loop_Slice_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_ToSlice(t *testing.T) { - - filter := func(u User) bool { return u.age <= 30 } - names := loop.Slice(loop.Convert(loop.Filter(loop.Of(users...), filter), User.Name)) - //[Bob Tom] - - assert.Equal(t, []string{"Bob", "Tom"}, names) -} diff --git a/internal/examples/loopexamples/loop_ToMapResolv_test.go b/internal/examples/loopexamples/loop_ToMapResolv_test.go deleted file mode 100644 index 10649742..00000000 --- a/internal/examples/loopexamples/loop_ToMapResolv_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/map_/resolv" - "github.com/m4gshm/gollections/op" -) - -func Test_ToMapResolv(t *testing.T) { - - var ageGroupedSortedNames map[string][]string - - ageGroupedSortedNames = loop.MapResolv(loop.Of(users...), func(u User) string { - return op.IfElse(u.age <= 30, "<=30", ">30") - }, User.Name, resolv.SortedSlice) - - //map[<=30:[Bob Tom] >30:[Alice Chris]] - - assert.Equal(t, []string{"Bob", "Tom"}, ageGroupedSortedNames["<=30"]) - assert.Equal(t, []string{"Alice", "Chris"}, ageGroupedSortedNames[">30"]) -} diff --git a/internal/examples/loopexamples/loop_filter_convert_reduce_test.go b/internal/examples/loopexamples/loop_filter_convert_reduce_test.go deleted file mode 100644 index d6fa2185..00000000 --- a/internal/examples/loopexamples/loop_filter_convert_reduce_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package loopexamples - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_Usage_Loop(t *testing.T) { - - even := func(i int) bool { return i%2 == 0 } - seq := loop.Convert(loop.Filter(loop.Of(1, 2, 3, 4), even), strconv.Itoa) - var result []string = seq.Slice() //[2 4] - - assert.Equal(t, []string{"2", "4"}, result) - -} diff --git a/internal/examples/loopexamples/loop_iterating_go_1_22_test.go b/internal/examples/loopexamples/loop_iterating_go_1_22_test.go deleted file mode 100644 index 4787c79d..00000000 --- a/internal/examples/loopexamples/loop_iterating_go_1_22_test.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build goexperiment.rangefunc - -package loopexamples - -import ( - "testing" - - "github.com/m4gshm/gollections/loop/range_" -) - -func Test_Iterating_Rangefunc(t *testing.T) { - - for i := range range_.Of(0, 100).All { - doOp(i) - } - -} diff --git a/internal/examples/loopexamples/loop_iterating_test.go b/internal/examples/loopexamples/loop_iterating_test.go deleted file mode 100644 index fa5734a4..00000000 --- a/internal/examples/loopexamples/loop_iterating_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/range_" -) - -func Test_Iterating_EmbeddedFor(t *testing.T) { - - next := range_.Of(0, 100) - for i, ok := next(); ok; i, ok = next() { - doOp(i) - } - -} - -func Test_Iterating_EmbeddedFor2(t *testing.T) { - - for next, i, ok := range_.Of(0, 100).Crank(); ok; i, ok = next() { - doOp(i) - } - -} - -func Test_Iterating_ForEach(t *testing.T) { - - range_.Of(0, 100).ForEach(doOp) - -} - -func Test_Iterating_For(t *testing.T) { - - range_.Of(0, 100).For(func(i int) error { - if i > 22 { - return loop.Break - } - doOp(i) - return loop.Continue - }) - -} - -func doOp(i int) { - -} diff --git a/internal/examples/loopexamples/range_Closed_test.go b/internal/examples/loopexamples/range_Closed_test.go deleted file mode 100644 index 983ccbba..00000000 --- a/internal/examples/loopexamples/range_Closed_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop/range_" -) - -func Test_RangeClosed(t *testing.T) { - - var increasing = range_.Closed(-1, 3).Slice() //[]int{-1, 0, 1, 2, 3} - var decreasing = range_.Closed('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b', 'a'} - var one = range_.Closed(1, 1).Slice() //[]int{1} - - assert.Equal(t, []int{-1, 0, 1, 2, 3}, increasing) - assert.Equal(t, []rune{'e', 'd', 'c', 'b', 'a'}, decreasing) - assert.Equal(t, []int{1}, one) -} diff --git a/internal/examples/loopexamples/range_Of_test.go b/internal/examples/loopexamples/range_Of_test.go deleted file mode 100644 index 6d04aa96..00000000 --- a/internal/examples/loopexamples/range_Of_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/m4gshm/gollections/loop/range_" - "github.com/stretchr/testify/assert" -) - -func Test_RangeOf(t *testing.T) { - - var increasing = range_.Of(-1, 3).Slice() //[]int{-1, 0, 1, 2} - var decreasing = range_.Of('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b'} - var nothing = range_.Of(1, 1).Slice() //nil - - assert.Equal(t, []int{-1, 0, 1, 2}, increasing) - assert.Equal(t, []rune{'e', 'd', 'c', 'b'}, decreasing) - assert.Nil(t, nothing) -} diff --git a/internal/examples/loopexamples/sum_Of_test.go b/internal/examples/loopexamples/sum_Of_test.go deleted file mode 100644 index 5ecc3dbb..00000000 --- a/internal/examples/loopexamples/sum_Of_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package loopexamples - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/sum" -) - -func Test_Sum(t *testing.T) { - - var sum = sum.Of(loop.Of(1, 2, 3, 4, 5, 6)) //21 - - assert.Equal(t, 21, sum) -} diff --git a/internal/examples/loopexamples/type.go b/internal/examples/loopexamples/type.go deleted file mode 100644 index 73af1d67..00000000 --- a/internal/examples/loopexamples/type.go +++ /dev/null @@ -1,8 +0,0 @@ -package loopexamples - -type ( - Loop[T any] func() (element T, ok bool) - KVLoop[K, V any] func() (key K, value V, ok bool) - BreakLoop[T any] func() (element T, ok bool, err error) - BreakKVLoop[K, V any] func() (key K, value V, ok bool, err error) -) diff --git a/internal/examples/loopexamples/type_User.go b/internal/examples/loopexamples/type_User.go deleted file mode 100644 index fe59a46d..00000000 --- a/internal/examples/loopexamples/type_User.go +++ /dev/null @@ -1,27 +0,0 @@ -package loopexamples - -type User struct { - name string - age int - roles []Role -} - -type Role struct { - name string -} - -func (u Role) Name() string { - return u.name -} - -func (u User) Name() string { - return u.name -} - -func (u User) Age() int { - return u.age -} - -func (u User) Roles() []Role { - return u.roles -} diff --git a/internal/examples/mapexamples/map_examples_test.go b/internal/examples/mapexamples/map_examples_test.go index b093f313..2a0b1b19 100644 --- a/internal/examples/mapexamples/map_examples_test.go +++ b/internal/examples/mapexamples/map_examples_test.go @@ -8,7 +8,6 @@ import ( "github.com/m4gshm/gollections/convert/ptr" "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/map_/clone" - "github.com/m4gshm/gollections/map_/group" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/clone/sort" ) @@ -40,29 +39,6 @@ func Test_ValuesConverted(t *testing.T) { assert.Equal(t, slice.Of("1_first", "2_second", "3_third"), sort.Asc(values)) } -type rows[T any] struct { - in []T - cursor int -} - -func (r *rows[T]) hasNext() bool { return r.cursor < len(r.in) } -func (r *rows[T]) next() (T, error) { e := r.in[r.cursor]; r.cursor++; return e, nil } - -func Test_OfLoop(t *testing.T) { - stream := &rows[int]{slice.Of(1, 2, 3), 0} - result, _ := map_.OfLoop( - stream, - (*rows[int]).hasNext, - func(r *rows[int]) (bool, int, error) { - n, err := r.next() - return n%2 == 0, n, err - }, - ) - - assert.Equal(t, 2, result[true]) - assert.Equal(t, 1, result[false]) -} - func Test_Generate(t *testing.T) { counter := 0 result, _ := map_.Generate(func() (bool, int, bool, error) { @@ -73,18 +49,3 @@ func Test_Generate(t *testing.T) { assert.Equal(t, 2, result[true]) assert.Equal(t, 1, result[false]) } - -func Test_GroupOfLoop(t *testing.T) { - stream := &rows[int]{slice.Of(1, 2, 3), 0} - result, _ := group.OfLoop( - stream, - (*rows[int]).hasNext, - func(r *rows[int]) (bool, int, error) { - n, err := r.next() - return n%2 == 0, n, err - }, - ) - - assert.Equal(t, slice.Of(2), result[true]) - assert.Equal(t, slice.Of(1, 3), result[false]) -} diff --git a/internal/examples/seqexamples/seq2_Group_test.go b/internal/examples/seqexamples/seq2_Group_test.go index 104dc665..fd2fb2f0 100644 --- a/internal/examples/seqexamples/seq2_Group_test.go +++ b/internal/examples/seqexamples/seq2_Group_test.go @@ -1,7 +1,6 @@ package seqexamples import ( - "iter" "testing" "github.com/stretchr/testify/assert" @@ -15,8 +14,8 @@ import ( func Test_Group(t *testing.T) { - var users iter.Seq[User] = seq.Of(users...) - var groups iter.Seq2[string, User] = seq.ToSeq2(users, func(u User) (string, User) { + var users seq.Seq[User] = seq.Of(users...) + var groups seq.Seq2[string, User] = seq.ToSeq2(users, func(u User) (string, User) { return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30"), u }) var ageGroups map[string][]User = seq2.Group(groups) diff --git a/internal/examples/seqexamples/seq2_Series_test.go b/internal/examples/seqexamples/seq2_Series_test.go index 17ba5430..7bf4daa6 100644 --- a/internal/examples/seqexamples/seq2_Series_test.go +++ b/internal/examples/seqexamples/seq2_Series_test.go @@ -11,7 +11,10 @@ import ( func Test_Series(t *testing.T) { var numbers, factorials []int - for i, n := range seq2.Series(1, func(i int, prev int) (int, bool) { return i * prev, i <= 5 }) { + next := func(i, prev int) (int, bool) { + return i * prev, i <= 5 + } + for i, n := range seq2.Series(1, next) { numbers = append(numbers, i) factorials = append(factorials, n) } diff --git a/internal/examples/seqexamples/seq_Filter_test.go b/internal/examples/seqexamples/seq_Filter_test.go index 906c2e5c..f3880bfd 100644 --- a/internal/examples/seqexamples/seq_Filter_test.go +++ b/internal/examples/seqexamples/seq_Filter_test.go @@ -15,7 +15,7 @@ func Test_Filter(t *testing.T) { var f1 = seq.Slice(seq.Filter(seq.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11)))) //[]int{1, 7, 11} - var f2 = seq.Slice(seq.Filter(seq.Of(1, 3, 5, 7, 9, 11), exclude.All(1, 7, 11))) + var f2 = seq.Of(1, 3, 5, 7, 9, 11).Filter(exclude.All(1, 7, 11)).Slice() //[]int{3, 5, 9} assert.Equal(t, []int{1, 7, 11}, f1) diff --git a/internal/examples/seqexamples/seq_First_test.go b/internal/examples/seqexamples/seq_First_test.go index c09834d4..6c84b475 100644 --- a/internal/examples/seqexamples/seq_First_test.go +++ b/internal/examples/seqexamples/seq_First_test.go @@ -15,4 +15,10 @@ func Test_First(t *testing.T) { assert.True(t, ok) assert.Equal(t, 7, result) + + //or + result, ok = seq.Of(1, 3, 5, 7, 9, 11).First(more.Than(5)) //7, true + + assert.True(t, ok) + assert.Equal(t, 7, result) } diff --git a/internal/examples/seqexamples/seq_Flat_test.go b/internal/examples/seqexamples/seq_Flat_test.go index 8a8263ab..40affef7 100644 --- a/internal/examples/seqexamples/seq_Flat_test.go +++ b/internal/examples/seqexamples/seq_Flat_test.go @@ -11,7 +11,8 @@ import ( func Test_Flat(t *testing.T) { - var i []int = seq.Slice(seq.Flat(seq.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), as.Is)) + twoDimensions := [][]int{{1, 2, 3}, {4}, {5, 6}} + var i []int = seq.Slice(seq.Flat(seq.Of(twoDimensions...), as.Is)) //[]int{1, 2, 3, 4, 5, 6} assert.Equal(t, []int{1, 2, 3, 4, 5, 6}, i) diff --git a/internal/examples/seqexamples/seq_Head_test.go b/internal/examples/seqexamples/seq_Head_test.go index 74ccd93b..6d5a10f8 100644 --- a/internal/examples/seqexamples/seq_Head_test.go +++ b/internal/examples/seqexamples/seq_Head_test.go @@ -14,4 +14,10 @@ func Test_Head(t *testing.T) { assert.True(t, ok) assert.Equal(t, 1, result) + + //or + result, ok = seq.Of(1, 3, 5, 7, 9, 11).Head() //1, true + + assert.True(t, ok) + assert.Equal(t, 1, result) } diff --git a/internal/examples/seqexamples/seq_OfNextPush_test.go b/internal/examples/seqexamples/seq_OfNextPush_test.go index e1a3b610..3b0450f8 100644 --- a/internal/examples/seqexamples/seq_OfNextPush_test.go +++ b/internal/examples/seqexamples/seq_OfNextPush_test.go @@ -13,7 +13,8 @@ func Test_rows_OfNext(t *testing.T) { var rows sql.Rows = selectUsers() - rowSeq := seqe.OfNext(rows.Next, func(u *User) error { return rows.Scan(&u.name, &u.age) }) + getUser := func(u *User) error { return rows.Scan(&u.name, &u.age) } + rowSeq := seqe.OfNext(rows.Next, getUser) usersByAge, err := seqe.Group(rowSeq, User.Age, as.Is) assert.Equal(t, 1, len(usersByAge)) diff --git a/internal/examples/seqexamples/seq_Of_test.go b/internal/examples/seqexamples/seq_Of_test.go index 25409d64..910f4ee8 100644 --- a/internal/examples/seqexamples/seq_Of_test.go +++ b/internal/examples/seqexamples/seq_Of_test.go @@ -1,7 +1,6 @@ package seqexamples import ( - "iter" "testing" "github.com/m4gshm/gollections/seq" @@ -13,15 +12,15 @@ import ( func Test_SeqOf(t *testing.T) { var ( - ints iter.Seq[int] = seq.Of(1, 2, 3) - pairs iter.Seq2[string, int] = seq2.OfMap(map[string]int{ + ints seq.Seq[int] = seq.Of(1, 2, 3) + pairs seq.Seq2[string, int] = seq2.OfMap(map[string]int{ "first": 1, "second": 2, "third": 3, }) ) - assert.Equal(t, []int{3, 2, 1}, sort.Desc(seq.Slice(seq2.Values(pairs)))) - assert.Equal(t, []string{"first", "second", "third"}, sort.Asc(seq.Slice(seq2.Keys(pairs)))) - assert.Equal(t, []int{1, 2, 3}, seq.Slice(ints)) + assert.Equal(t, []int{3, 2, 1}, sort.Desc(pairs.Values().Slice())) + assert.Equal(t, []string{"first", "second", "third"}, sort.Asc(pairs.Keys().Slice())) + assert.Equal(t, []int{1, 2, 3}, ints.Slice()) } diff --git a/internal/examples/seqexamples/seq_ReduceOK_test.go b/internal/examples/seqexamples/seq_ReduceOK_test.go index c13123b5..5cf79d24 100644 --- a/internal/examples/seqexamples/seq_ReduceOK_test.go +++ b/internal/examples/seqexamples/seq_ReduceOK_test.go @@ -11,12 +11,15 @@ import ( func Test_Loop_ReduceOKSum(t *testing.T) { adder := func(i1, i2 int) int { return i1 + i2 } - sum, ok := seq.ReduceOK(seq.Of(1, 2, 3, 4, 5, 6), adder) //21, true + assert.True(t, ok) + assert.Equal(t, 21, sum) + + //or emptyLoop := seq.Of[int]() - sum, ok = seq.ReduceOK(emptyLoop, adder) + sum, ok = emptyLoop.ReduceOK(adder) //0, false assert.False(t, ok) diff --git a/internal/examples/seqexamples/seq_Reduce_test.go b/internal/examples/seqexamples/seq_Reduce_test.go index 5ec1fd33..0a1ff80c 100644 --- a/internal/examples/seqexamples/seq_Reduce_test.go +++ b/internal/examples/seqexamples/seq_Reduce_test.go @@ -10,8 +10,16 @@ import ( func Test_Loop_ReduceSum(t *testing.T) { - var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 + i2 }) + adder := func(i1, i2 int) int { return i1 + i2 } + var sum = seq.Reduce(seq.Of(1, 2, 3, 4, 5, 6), adder) //21 assert.Equal(t, 21, sum) + + //or + sum = seq.Of(1, 2, 3, 4, 5, 6).Reduce(adder) + //21 + + assert.Equal(t, 21, sum) + } diff --git a/internal/examples/seqexamples/seq_Skip_test.go b/internal/examples/seqexamples/seq_Skip_test.go index f15bd808..b20ebae1 100644 --- a/internal/examples/seqexamples/seq_Skip_test.go +++ b/internal/examples/seqexamples/seq_Skip_test.go @@ -10,7 +10,13 @@ import ( func Test_Skip(t *testing.T) { - var i []int = seq.Slice(seq.Skip(4, seq.Of(1, 3, 5, 7, 9, 11))) + i := seq.Slice(seq.Skip(4, seq.Of(1, 3, 5, 7, 9, 11))) + //[]int{9, 11} + + assert.Equal(t, []int{9, 11}, i) + + //or + i = seq.Of(1, 3, 5, 7, 9, 11).Skip(4).Slice() //[]int{9, 11} assert.Equal(t, []int{9, 11}, i) diff --git a/internal/examples/seqexamples/seq_Slice_test.go b/internal/examples/seqexamples/seq_Slice_test.go index 8e26c144..ae64bf83 100644 --- a/internal/examples/seqexamples/seq_Slice_test.go +++ b/internal/examples/seqexamples/seq_Slice_test.go @@ -11,7 +11,15 @@ import ( func Test_ToSlice(t *testing.T) { filter := func(u User) bool { return u.age <= 30 } - names := seq.Slice(seq.Convert(seq.Filter(seq.Of(users...), filter), User.Name)) + less30Names := seq.Convert(seq.Of(users...).Filter(filter), User.Name) + + names := seq.Slice(less30Names) + //[Bob Tom] + + assert.Equal(t, []string{"Bob", "Tom"}, names) + + //or + names = less30Names.Slice() //[Bob Tom] assert.Equal(t, []string{"Bob", "Tom"}, names) diff --git a/internal/examples/seqexamples/seq_Top_test.go b/internal/examples/seqexamples/seq_Top_test.go index 5f18f501..abc206a4 100644 --- a/internal/examples/seqexamples/seq_Top_test.go +++ b/internal/examples/seqexamples/seq_Top_test.go @@ -10,7 +10,13 @@ import ( func Test_Top(t *testing.T) { - var i []int = seq.Slice(seq.Top(4, seq.Of(1, 3, 5, 7, 9, 11))) + i := seq.Slice(seq.Top(4, seq.Of(1, 3, 5, 7, 9, 11))) + //[]int{1, 3, 5, 7} + + assert.Equal(t, []int{1, 3, 5, 7}, i) + + //or + i = seq.Of(1, 3, 5, 7, 9, 11).Top(4).Slice() //[]int{1, 3, 5, 7} assert.Equal(t, []int{1, 3, 5, 7}, i) diff --git a/internal/examples/sliceexamples/complex_slice_examples/complex_slice_examples_test.go b/internal/examples/sliceexamples/complex_slice_examples/complex_slice_examples_test.go index d747c3c6..e3261b95 100644 --- a/internal/examples/sliceexamples/complex_slice_examples/complex_slice_examples_test.go +++ b/internal/examples/sliceexamples/complex_slice_examples/complex_slice_examples_test.go @@ -9,7 +9,6 @@ import ( "github.com/m4gshm/gollections/collection/immutable/set" "github.com/m4gshm/gollections/seq" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/predicate/eq" "github.com/m4gshm/gollections/predicate/match" "github.com/m4gshm/gollections/predicate/not" @@ -107,69 +106,67 @@ userLoop: assert.Equal(t, "Alice", legacyAlice.Name()) } -func Benchmark_FindFirsManager_Predicate_ContainsConverted(b *testing.B) { +func Benchmark_FindFirsManager_Predicate_WhereAnyWhereEq(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, where.Any(User.Roles, where.Eq(Role.Name, "Manager"))) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Predicate_HasAnyConverted(b *testing.B) { +func Benchmark_FindFirsManager_Predicate_MatchAnyMatchToEqTo(b *testing.B) { for i := 0; i < b.N; i++ { - alice, ok := slice.First(users, match.Any(User.Roles, match.To(Role.Name, func(roleName string) bool { return roleName == "manager" }))) + alice, ok := slice.First(users, match.Any(User.Roles, match.To(Role.Name, eq.To("Manager")))) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Set(b *testing.B) { +func Benchmark_FindFirsManager_Predicate_MatchAnyMatchToFunc(b *testing.B) { for i := 0; i < b.N; i++ { - alice, ok := slice.First(users, func(user User) bool { - return set.Convert(set.New(user.Roles()), Role.Name).HasAny(eq.To("Manager")) - }) + alice, ok := slice.First(users, match.Any(User.Roles, match.To(Role.Name, func(roleName string) bool { return roleName == "Manager" }))) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Slice(b *testing.B) { +func Benchmark_FindFirsManager_Set(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, func(user User) bool { - return slice.Contains(slice.Convert(user.Roles(), Role.Name), "Manager") + return set.Convert(set.New(user.Roles()), Role.Name).HasAny(eq.To("Manager")) }) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Loop(b *testing.B) { +func Benchmark_FindFirsManager_Slice(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, func(user User) bool { - return loop.Contains(loop.Convert(loop.Of(user.Roles()...), Role.Name), "Manager") + return slice.Contains(slice.Convert(user.Roles(), Role.Name), "Manager") }) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Loop_HasAny(b *testing.B) { +func Benchmark_FindFirsManager_Seq(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, func(user User) bool { - return loop.Convert(loop.Of(user.Roles()...), Role.Name).HasAny(eq.To("Manager")) + return seq.Contains(seq.Convert(seq.Of(user.Roles()...), Role.Name), "Manager") }) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Seq(b *testing.B) { +func Benchmark_FindFirsManager_Seq_HasAnyEqTo(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, func(user User) bool { - return seq.Contains(seq.Convert(seq.Of(user.Roles()...), Role.Name), "Manager") + return seq.Convert(seq.Of(user.Roles()...), Role.Name).HasAny(eq.To("Manager")) }) _, _ = alice, ok } } -func Benchmark_FindFirsManager_Seq_HasAny(b *testing.B) { +func Benchmark_FindFirsManager_Seq_HasAnyFunc(b *testing.B) { for i := 0; i < b.N; i++ { alice, ok := slice.First(users, func(user User) bool { - return seq.HasAny(seq.Convert(seq.Of(user.Roles()...), Role.Name), eq.To("Manager")) + return seq.Convert(seq.Of(user.Roles()...), Role.Name).HasAny(eq.To("Manager")) }) _, _ = alice, ok } @@ -220,13 +217,6 @@ func Benchmark_AggregateFilteredRoles_Slice(b *testing.B) { } } -func Benchmark_AggregateFilteredRoles_Loop(b *testing.B) { - for i := 0; i < b.N; i++ { - roleNamesExceptManager := loop.Filter(loop.Convert(loop.Flat(loop.Of(users...), User.Roles), Role.Name), not.Eq("Manager")) - _ = loop.Slice(roleNamesExceptManager) - } -} - func Benchmark_AggregateFilteredRoles_Seq_FlatSeq(b *testing.B) { for i := 0; i < b.N; i++ { roles := seq.FlatSeq(seq.Of(users...), func(u User) seq.Seq[Role] { return seq.Of(u.roles...) }) diff --git a/internal/seq2/api.go b/internal/seq2/api.go new file mode 100644 index 00000000..9d47b7f0 --- /dev/null +++ b/internal/seq2/api.go @@ -0,0 +1,263 @@ +// Package seq2 extends [iter.Seq2] API with convering, filtering, and reducing functionality. +package seq2 + +import ( + converte "github.com/m4gshm/gollections/break/kv/convert" + "github.com/m4gshm/gollections/c" + "github.com/m4gshm/gollections/kv" + "github.com/m4gshm/gollections/kv/convert" + "github.com/m4gshm/gollections/kv/predicate" + "github.com/m4gshm/gollections/map_/resolv" +) + +// Seq is an iterator-function that allows to iterate over elements of a sequence, such as slice. +type Seq[T any] = func(func(T) bool) + +// SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. +// It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. +// At each iteration step, it is necessary to check for the occurrence of an error. +// +// for e, err := range seqence { +// if err != nil { +// break +// } +// ... +// } +type SeqE[T any] = Seq2[T, error] + +// Seq2 is an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. +// It is used to iterate over slice index/value pairs or map key/value pairs. +type Seq2[K, V any] = func(func(K, V) bool) + +// Union combines several sequences into one. +func Union[S ~Seq2[K, V], K, V any](seq ...S) Seq2[K, V] { + return func(yield func(K, V) bool) { + for _, s := range seq { + if s != nil { + for k, v := range s { + if !yield(k, v) { + return + } + } + } + } + } +} + +// Head returns the first key\value pair. +func Head[S ~Seq2[K, V], K, V any](seq S) (k K, v V, ok bool) { + return First(seq, func(K, V) bool { return true }) +} + +// HasAny checks whether the seq contains a key\value pair that satisfies the condition. +func HasAny[S ~Seq2[K, V], K, V any](seq S, condition func(K, V) bool) bool { + _, _, ok := First(seq, condition) + return ok +} + +// First returns the first key\value pair that satisfies the condition. +func First[S ~Seq2[K, V], K, V any](seq S, condition func(K, V) bool) (k K, v V, ok bool) { + if seq == nil || condition == nil { + return + } + seq(func(oneK K, oneV V) bool { + if condition(oneK, oneV) { + k = oneK + v = oneV + ok = true + return false + } + return true + }) + return +} + +// Firstt returns the first key\value pair that satisfies the condition. +func Firstt[S ~Seq2[K, V], K, V any](seq S, condition func(K, V) (bool, error)) (k K, v V, ok bool, err error) { + if seq == nil || condition == nil { + return + } + seq(func(oneK K, oneV V) bool { + ok, err = condition(oneK, oneV) + if ok { + k = oneK + v = oneV + return false + } else if err != nil { + return false + } + return true + }) + return k, v, ok, err +} + +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func Filter[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) Seq2[K, V] { + return func(yield func(K, V) bool) { + if seq == nil || filter == nil { + return + } + seq(func(k K, v V) bool { + if filter(k, v) { + return yield(k, v) + } + return true + }) + } +} + +// Filt creates an erroreable iterator that iterates only those key\value pairs for which the 'filter' function returns true. +func Filt[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) (bool, error)) Seq2[c.KV[K, V], error] { + return func(yield func(c.KV[K, V], error) bool) { + if seq == nil || filter == nil { + return + } + seq(func(k K, v V) bool { + if ok, err := filter(k, v); ok || err != nil { + return yield(kv.New(k, v), err) + } + return true + }) + } +} + +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function. +func FilterKey[S ~Seq2[K, V], K, V any](seq S, filter func(K) bool) Seq2[K, V] { + return Filter(seq, predicate.Key[V](filter)) +} + +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function. +func FilterValue[S ~Seq2[K, V], K, V any](seq S, filter func(V) bool) Seq2[K, V] { + return Filter(seq, predicate.Value[K](filter)) +} + +// ConvertKey returns a seq that applies the 'converter' function to keys. +func ConvertKey[S ~Seq2[Kfrom, V], Kfrom, Kto, V any](seq S, converter func(Kfrom) Kto) Seq2[Kto, V] { + return Convert(seq, convert.Key[V](converter)) +} + +// ConvKey returns a seq that applies the 'converter' function to keys. +func ConvKey[S ~Seq2[Kfrom, V], Kfrom, Kto, V any](seq S, converter func(Kfrom) (Kto, error)) SeqE[c.KV[Kto, V]] { + return Conv(seq, converte.Key[V](converter)) +} + +// ConvertValue returns a seq that applies the 'converter' function to values. +func ConvertValue[S ~Seq2[K, Vfrom], K, Vfrom, Vto any](seq S, converter func(Vfrom) Vto) Seq2[K, Vto] { + return Convert(seq, convert.Value[K](converter)) +} + +// ConvValue returns a seq that applies the 'converter' function to values. +func ConvValue[S ~Seq2[K, Vfrom], K, Vfrom, Vto any](seq S, converter func(Vfrom) (Vto, error)) SeqE[c.KV[K, Vto]] { + return Conv(seq, converte.Value[K](converter)) +} + +// Convert creates an iterator that applies the 'converter' function to each iterable key\value pair. +func Convert[S ~Seq2[Kfrom, Vfrom], Kfrom, Vfrom, Kto, Vto any](seq S, converter func(Kfrom, Vfrom) (Kto, Vto)) Seq2[Kto, Vto] { + return func(consumer func(Kto, Vto) bool) { + if seq == nil || converter == nil { + return + } + seq(func(k Kfrom, v Vfrom) bool { + return consumer(converter(k, v)) + }) + } +} + +// Conv creates an errorable seq that applies the 'converter' function to the iterable key\value pairs. +func Conv[S ~Seq2[Kfrom, Vfrom], Kfrom, Vfrom, Kto, Vto any](seq S, converter func(Kfrom, Vfrom) (Kto, Vto, error)) SeqE[c.KV[Kto, Vto]] { + return func(consumer func(c.KV[Kto, Vto], error) bool) { + if seq == nil || converter == nil { + return + } + seq(func(k Kfrom, v Vfrom) bool { + kto, vto, err := converter(k, v) + return consumer(kv.New(kto, vto), err) + }) + } +} + +// Values converts a key/value pairs iterator to an iterator of just values. +func Values[S ~Seq2[K, V], K, V any](seq S) Seq[V] { + return func(yield func(V) bool) { + if seq == nil { + return + } + seq(func(_ K, v V) bool { + return yield(v) + }) + } +} + +// Keys converts a key/value pairs iterator to an iterator of just keys. +func Keys[S ~Seq2[K, V], K, V any](seq S) Seq[K] { + return func(yield func(K) bool) { + if seq == nil { + return + } + seq(func(k K, _ V) bool { + return yield(k) + }) + } +} + +// Group collects the elements of the 'seq' sequence into a new map. +func Group[S ~Seq2[K, V], K comparable, V any](seq S) map[K][]V { + return MapResolv(seq, resolv.Slice[K, V]) +} + +// MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values. +func MapResolv[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR) map[K]VR { + return AppendMapResolv(seq, resolver, nil) +} + +// MapResolvOrder collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values. +// Returns a slice with the keys ordered by the time they were added and the resolved key\value map. +func MapResolvOrder[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR) ([]K, map[K]VR) { + return AppendMapResolvOrder(seq, resolver, nil, nil) +} + +// AppendMapResolv collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values. +func AppendMapResolv[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR, dest map[K]VR) map[K]VR { + if seq == nil || resolver == nil { + return nil + } + if dest == nil { + dest = map[K]VR{} + } + seq(func(k K, v V) bool { + exists, ok := dest[k] + dest[k] = resolver(ok, k, exists, v) + return true + }) + return dest +} + +// AppendMapResolvOrder collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values +// Additionaly populates the 'order' slice by the keys ordered by the time they were added and the resolved key\value map. +func AppendMapResolvOrder[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR, order []K, dest map[K]VR) ([]K, map[K]VR) { + if seq == nil || resolver == nil { + return nil, nil + } + if dest == nil { + dest = map[K]VR{} + } + seq(func(k K, v V) bool { + exists, ok := dest[k] + dest[k] = resolver(ok, k, exists, v) + if !ok { + order = append(order, k) + } + return true + }) + return order, dest +} + +// TrackEach applies the 'consumer' function to the seq key\value pairs. +func TrackEach[S ~Seq2[K, V], K, V any](seq S, consumer func(K, V)) { + if seq == nil { + return + } + for k, v := range seq { + consumer(k, v) + } +} diff --git a/internal/seqe/api.go b/internal/seqe/api.go new file mode 100644 index 00000000..b52861ff --- /dev/null +++ b/internal/seqe/api.go @@ -0,0 +1,362 @@ +// Package seqe provides convering, filtering, and reducing operations for the [seq.SeqE] interface. +package seqe + +import ( + "github.com/m4gshm/gollections/predicate/always" +) + +// SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. +// It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. +// At each iteration step, it is necessary to check for the occurrence of an error. +// +// for e, err := range seqence { +// if err != nil { +// break +// } +// ... +// } +type SeqE[T any] = func(func(T, error) bool) + +// Union combines several sequences into one. +func Union[S ~SeqE[T], T any](seq ...S) SeqE[T] { + return func(yield func(T, error) bool) { + for _, s := range seq { + if s != nil { + for v, err := range s { + if !yield(v, err) { + return + } + } + } + } + } +} + +// Top returns a sequence of top n elements. +func Top[S ~SeqE[T], T any](n int, seq S) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil { + return + } + m := n + seq(func(t T, err error) bool { + if m == 0 { + return false + } + m-- + return yield(t, err) + }) + } +} + +// Skip returns the seq without first n elements. +func Skip[S ~SeqE[T], T any](n int, seq S) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil { + return + } + m := n + seq(func(t T, err error) bool { + if m == 0 { + return yield(t, err) + } + m-- + return true + }) + } +} + +// While cuts tail elements of the seq that don't match the filter. +func While[S ~SeqE[T], T any](seq S, filter func(T) bool) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil { + return + } + seq(func(t T, err error) bool { + if !filter(t) { + return false + } + return yield(t, err) + }) + } +} + +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func SkipWhile[S ~SeqE[T], T any](seq S, filter func(T) bool) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil { + return + } + started := false + seq(func(t T, err error) bool { + if !started && filter(t) { + return true + } + started = true + return yield(t, err) + }) + } +} + +// Head returns the first element. +func Head[S ~SeqE[T], T any](seq S) (v T, ok bool, err error) { + return First(seq, always.True) +} + +// First returns the first element that satisfies the condition. +func First[S ~SeqE[T], T any](seq S, predicate func(T) bool) (v T, ok bool, err error) { + if seq == nil || predicate == nil { + return + } + seq(func(one T, e error) bool { + if e != nil { + err = e + ok = false + return false + } else if predicate(one) { + v = one + ok = true + return false + } + return true + }) + return +} + +// Firstt returns the first element that satisfies the condition. +func Firstt[S ~SeqE[T], T any](seq S, predicate func(T) (bool, error)) (v T, ok bool, err error) { + if seq == nil || predicate == nil { + return v, false, nil + } + seq(func(one T, e error) bool { + if e != nil { + err = e + return false + } else if ok, err = predicate(one); ok { + v = one + return false + } else if err != nil { + return false + + } + return true + }) + return v, ok, err +} + +// Slice collects the elements of the 'seq' sequence into a new slice. +func Slice[S ~SeqE[T], T any](seq S) ([]T, error) { + return SliceCap(seq, 0) +} + +// SliceCap collects the elements of the 'seq' sequence into a new slice with predefined capacity. +func SliceCap[S ~SeqE[T], T any](seq S, capacity int) (out []T, err error) { + if capacity > 0 { + out = make([]T, 0, capacity) + } + return Append(seq, out) +} + +// Append collects the elements of the 'seq' sequence into the specified 'out' slice. +func Append[S ~SeqE[T], T any, TS ~[]T](seq S, out TS) (TS, error) { + if seq == nil { + return out, nil + } + var errOur error + seq(func(v T, e error) bool { + if e != nil { + errOur = e + return false + } + out = append(out, v) + return true + }) + return out, errOur +} + +// Reduce reduces the elements of the seq into one using the 'merge' function. +func Reduce[S ~SeqE[T], T any](seq S, merge func(T, T) T) (T, error) { + result, _, err := ReduceOK(seq, merge) + return result, err +} + +// ReduceOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func ReduceOK[S ~SeqE[T], T any](seq S, merge func(T, T) T) (result T, ok bool, err error) { + if seq == nil || merge == nil { + return result, false, nil + } + started := false + seq(func(v T, e error) bool { + if e != nil { + err = e + return false + } else if !started { + result = v + } else { + result = merge(result, v) + } + started = true + return true + }) + return result, started, err +} + +// Reducee reduces the elements of the seq into one using the 'merge' function. +func Reducee[S ~SeqE[T], T any](seq S, merge func(T, T) (T, error)) (T, error) { + result, _, err := ReduceeOK(seq, merge) + return result, err +} + +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func ReduceeOK[S ~SeqE[T], T any](seq S, merge func(T, T) (T, error)) (result T, ok bool, err error) { + if seq == nil || merge == nil { + return result, false, nil + } + started := false + seq(func(v T, e error) bool { + if e != nil { + err = e + return false + } else if !started { + result = v + } else { + result, err = merge(result, v) + if err != nil { + return false + } + } + started = true + return true + }) + return result, started, err +} + +// 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 of the 'seq' sequence. +func Accum[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) T) (accumulator T, err error) { + accumulator = first + if seq == nil || merge == nil { + return + } + seq(func(v T, e error) bool { + err = e + if err != nil { + return false + } + accumulator = merge(accumulator, v) + return true + }) + return +} + +// 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 of the 'seq' sequence. +func Accumm[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) (T, error)) (accumulator T, err error) { + accumulator = first + if seq == nil || merge == nil { + return accumulator, nil + } + seq(func(v T, e error) bool { + err = e + if err == nil { + accumulator, err = merge(accumulator, v) + } + return err == nil + }) + return accumulator, err +} + +// HasAny checks whether the seq contains an element that satisfies the condition. +func HasAny[S ~SeqE[T], T any](seq S, predicate func(T) bool) (bool, error) { + _, ok, err := First(seq, predicate) + return ok, err +} + +// Conv creates an errorable seq that applies the 'converter' function to the collection elements. +// The error should be checked at every iteration step, like: +// +// var integers iter.Seq2[int, error] +// ... +// for s, err := range seqe.Conv(integers, strconv.Itoa) { +// if err != nil { +// break +// } +// ... +// } +func Conv[S ~SeqE[From], From, To any](seq S, converter func(From) (To, error)) SeqE[To] { + return func(yield func(To, error) bool) { + if seq == nil || converter == nil { + return + } + seq(func(from From, err error) bool { + if err != nil { + var to To + return yield(to, err) + } + return yield(converter(from)) + }) + } +} + +// Convert creates an iterator that applies the 'converter' function to each iterable element. +func Convert[S ~SeqE[From], From, To any](seq S, converter func(From) To) SeqE[To] { + return func(yield func(To, error) bool) { + if seq == nil || converter == nil { + return + } + seq(func(from From, err error) bool { + if err != nil { + var to To + return yield(to, err) + } + return yield(converter(from), err) + }) + } +} + +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func Filter[S ~SeqE[T], T any](seq S, filter func(T) bool) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil || filter == nil { + return + } + seq(func(t T, err error) bool { + if err != nil || filter(t) { + return yield(t, err) + } + return true + }) + } +} + +// Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. +func Filt[S ~SeqE[T], T any](seq S, filter func(T) (bool, error)) SeqE[T] { + return func(yield func(T, error) bool) { + if seq == nil || filter == nil { + return + } + seq(func(t T, err error) bool { + if err != nil { + return yield(t, err) + } + if ok, err := filter(t); ok || err != nil { + return yield(t, err) + } + return true + }) + } +} + +// ForEach applies the 'consumer' function to the seq elements +func ForEach[T any](seq SeqE[T], consumer func(T)) error { + if seq == nil { + return nil + } + for v, err := range seq { + if err != nil { + return err + } + consumer(v) + } + return nil +} diff --git a/kv/collection/iface.go b/kv/collection/iface.go index addde6ee..b14b4120 100644 --- a/kv/collection/iface.go +++ b/kv/collection/iface.go @@ -1,9 +1,7 @@ package collection import ( - breakloop "github.com/m4gshm/gollections/break/kv/loop" "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/kv/loop" ) // Iterator provides iterate over key/value pairs @@ -12,20 +10,12 @@ type Iterator[K, V any] interface { // The ok result indicates whether the element was returned by the iterator. // If ok == false, then the iteration must be completed. Next() (key K, value V, ok bool) - c.Track[K, V] c.TrackEach[K, V] } -// Iterable is an iterator supplier interface -type Iterable[K, V any] interface { - Loop() loop.Loop[K, V] -} - // Collection is the base interface of associative collections type Collection[K comparable, V any, M map[K]V | map[K][]V] interface { - c.Track[K, V] c.TrackEach[K, V] - Iterable[K, V] c.MapFactory[K, V, M] Reduce(merge func(K, K, V, V) (K, V)) (K, V) @@ -34,25 +24,31 @@ type Collection[K comparable, V any, M map[K]V | map[K][]V] interface { } // Convertable provides limited kit of map transformation methods -type Convertable[K, V any] interface { - Convert(converter func(K, V) (K, V)) loop.Loop[K, V] - Conv(converter func(K, V) (K, V, error)) breakloop.Loop[K, V] - - ConvertKey(converter func(K) K) loop.Loop[K, V] - ConvertValue(converter func(V) V) loop.Loop[K, V] - - ConvKey(converter func(K) (K, error)) breakloop.Loop[K, V] - ConvValue(converter func(V) (V, error)) breakloop.Loop[K, V] +type Convertable[K, V any, + Seq2 ~func(yield func(K, V) bool), + SeqE ~func(yield func(c.KV[K, V], error) bool), +] interface { + Convert(converter func(K, V) (K, V)) Seq2 + Conv(converter func(K, V) (K, V, error)) SeqE + + ConvertKey(converter func(K) K) Seq2 + ConvertValue(converter func(V) V) Seq2 + + ConvKey(converter func(K) (K, error)) SeqE + ConvValue(converter func(V) (V, error)) SeqE } // Filterable provides limited kit of filering methods -type Filterable[K, V any] interface { - Filter(predicate func(K, V) bool) loop.Loop[K, V] - Filt(predicate func(K, V) (bool, error)) breakloop.Loop[K, V] - - FilterKey(predicate func(K) bool) loop.Loop[K, V] - FilterValue(predicate func(V) bool) loop.Loop[K, V] - - FiltKey(predicate func(K) (bool, error)) breakloop.Loop[K, V] - FiltValue(predicate func(V) (bool, error)) breakloop.Loop[K, V] +type Filterable[K, V any, + Seq2 ~func(yield func(K, V) bool), + SeqE ~func(yield func(c.KV[K, V], error) bool), +] interface { + Filter(predicate func(K, V) bool) Seq2 + Filt(predicate func(K, V) (bool, error)) SeqE + + FilterKey(predicate func(K) bool) Seq2 + FilterValue(predicate func(V) bool) Seq2 + + FiltKey(predicate func(K) (bool, error)) SeqE + FiltValue(predicate func(V) (bool, error)) SeqE } diff --git a/kv/loop/api.go b/kv/loop/api.go deleted file mode 100644 index 1b19db8f..00000000 --- a/kv/loop/api.go +++ /dev/null @@ -1,214 +0,0 @@ -// Package loop provides helpers for loop operation over key/value pairs and iterator implementations -// -// Deprecated: use the [github.com/m4gshm/gollections/seq], [github.com/m4gshm/gollections/seqe], [github.com/m4gshm/gollections/seq2] packages API instead. -package loop - -import ( - "github.com/m4gshm/gollections/break/kv/loop" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/map_/resolv" -) - -// New makes a loop from an abstract source -func New[S, K, V any](source S, hasNext func(S) bool, getNext func(S) (K, V)) Loop[K, V] { - return func() (k K, v V, ok bool) { - if hasNext(source) { - k, v = getNext(source) - return k, v, true - } - return k, v, false - } -} - -// All is an adapter for the next function for iterating by `for ... range`. -func All[K, V any](next func() (K, V, bool), consumer func(K, V) bool) { - for k, v, ok := next(); ok && consumer(k, v); k, v, ok = next() { - } -} - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. -func Track[I, T any](next func() (I, T, bool), consumer func(I, T) error) error { - if next == nil { - return nil - } - for p, v, ok := next(); ok; p, v, ok = next() { - if err := consumer(p, v); err == c.Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - -// TrackEach applies the 'consumer' function to position/element pairs retrieved by the 'next' function -func TrackEach[I, T any](next func() (I, T, bool), consumer func(I, T)) { - if next == nil { - return - } - for p, v, ok := next(); ok; p, v, ok = next() { - consumer(p, v) - } -} - -// Group collects sets of values grouped by keys obtained by passing a key/value iterator -func Group[K comparable, V any](next func() (K, V, bool)) map[K][]V { - return MapResolv(next, resolv.Slice[K, V]) -} - -// Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func Reduce[K, V any](next func() (K, V, bool), merge func(K, K, V, V) (K, V)) (rk K, rv V) { - rk, rv, _ = ReduceOK(next, merge) - return rk, rv -} - -// ReduceOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceOK[K, V any](next func() (K, V, bool), merge func(K, K, V, V) (K, V)) (rk K, rv V, ok bool) { - if next == nil { - return rk, rv, false - } - k, v, ok := next() - if !ok { - return k, v, false - } - rk, rv = k, v - for k, v, ok := next(); ok; k, v, ok = next() { - rk, rv = merge(rk, k, rv, v) - } - return rk, rv, true -} - -// Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero values of 'K', 'V' types are returned. -func Reducee[K, V any](next func() (K, V, bool), merge func(K, K, V, V) (K, V, error)) (rk K, rv V, err error) { - rk, rv, _, err = ReduceeOK(next, merge) - return rk, rv, err -} - -// ReduceeOK reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceeOK[K, V any](next func() (K, V, bool), merge func(K, K, V, V) (K, V, error)) (rk K, rv V, ok bool, err error) { - if next == nil { - return rk, rv, false, nil - } - k, v, ok := next() - if !ok { - return rk, rv, false, nil - } - rk, rv = k, v - for { - if k, v, ok := next(); !ok { - return rk, rv, true, nil - } else if rk, rv, err = merge(rk, k, rv, v); err != nil { - return rk, rv, true, err - } - } -} - -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func HasAny[K, V any](next func() (K, V, bool), predicate func(K, V) bool) bool { - _, _, ok := First(next, predicate) - return ok -} - -// HasAnyy finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func HasAnyy[K, V any](next func() (K, V, bool), predicate func(K, V) (bool, error)) (bool, error) { - _, _, ok, err := Firstt(next, predicate) - return ok, err -} - -// First returns the first key/value pair that satisfies the condition of the 'predicate' function -func First[K, V any](next func() (K, V, bool), predicate func(K, V) bool) (K, V, bool) { - for { - if k, v, ok := next(); !ok { - return k, v, false - } else if ok := predicate(k, v); ok { - return k, v, true - } - } -} - -// Firstt returns the first key/value pair that satisfies the condition of the 'predicate' function -func Firstt[K, V any](next func() (K, V, bool), predicate func(K, V) (bool, error)) (K, V, bool, error) { - for { - if k, v, ok := next(); !ok { - return k, v, false, nil - } else if ok, err := predicate(k, v); err != nil || ok { - return k, v, ok, err - } - } -} - -// Convert creates a loop that applies the 'converter' function to iterable key\values. -func Convert[K, V any, KOUT, VOUT any](next func() (K, V, bool), converter func(K, V) (KOUT, VOUT)) Loop[KOUT, VOUT] { - if next == nil { - return nil - } - return func() (k2 KOUT, v2 VOUT, ok bool) { - if k, v, ok := next(); ok { - k2, v2 = converter(k, v) - return k2, v2, true - } - return k2, v2, false - } -} - -// Conv creates a loop that applies the 'converter' function to iterable key\values. -func Conv[K, V any, KOUT, VOUT any](next func() (K, V, bool), converter func(K, V) (KOUT, VOUT, error)) loop.Loop[KOUT, VOUT] { - return loop.Conv(loop.From(next), converter) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filter[K, V any](next func() (K, V, bool), filter func(K, V) bool) Loop[K, V] { - if next == nil { - return nil - } - return func() (K, V, bool) { - return First(next, filter) - } -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filt[K, V any](next func() (K, V, bool), filter func(K, V) (bool, error)) loop.Loop[K, V] { - return loop.Filt(loop.From(next), filter) -} - -// MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values -func MapResolv[K comparable, V, VR any](next func() (K, V, bool), resolver func(bool, K, VR, V) VR) map[K]VR { - if next == nil { - return nil - } - m := map[K]VR{} - for k, v, ok := next(); ok; k, v, ok = next() { - exists, ok := m[k] - m[k] = resolver(ok, k, exists, v) - } - return m -} - -// Map collects key\value elements into a new map by iterating over the elements -func Map[K comparable, V any](next func() (K, V, bool)) map[K]V { - return MapResolv(next, resolv.First[K, V]) -} - -// Slice collects key\value elements to a slice by iterating over the elements -func Slice[K, V, T any](next func() (K, V, bool), converter func(K, V) T) []T { - if next == nil { - return nil - } - s := []T{} - for key, val, ok := next(); ok; key, val, ok = next() { - s = append(s, converter(key, val)) - } - return s -} - -// Crank rertieves next key\value from the 'next' function, returns the function, element, successfully flag. -func Crank[K, V any](next func() (K, V, bool)) (n Loop[K, V], k K, v V, ok bool) { - if next != nil { - k, v, ok = next() - } - return next, k, v, ok -} diff --git a/kv/loop/group/api.go b/kv/loop/group/api.go deleted file mode 100644 index de65d344..00000000 --- a/kv/loop/group/api.go +++ /dev/null @@ -1,11 +0,0 @@ -// Package group provides short aliases for functions thath are used to group key/value pairs retrieved by a loop -package group - -import ( - "github.com/m4gshm/gollections/kv/loop" -) - -// Of is a short alias for loop.Group -func Of[K comparable, V any](next func() (K, V, bool)) map[K][]V { - return loop.Group(next) -} diff --git a/kv/loop/group/api_test.go b/kv/loop/group/api_test.go deleted file mode 100644 index d7325140..00000000 --- a/kv/loop/group/api_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package group - -import ( - "testing" - - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" - - "github.com/stretchr/testify/assert" -) - -func Test_group_odd_even(t *testing.T) { - - var ( - even = func(v int) bool { return v%2 == 0 } - groups = Of(loop.KeyValue(loop.Of(1, 1, 2, 4, 3, 1), even, as.Is[int])) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} diff --git a/kv/loop/loop.go b/kv/loop/loop.go deleted file mode 100644 index 62ac7768..00000000 --- a/kv/loop/loop.go +++ /dev/null @@ -1,109 +0,0 @@ -package loop - -import ( - breakkvloop "github.com/m4gshm/gollections/break/kv/loop" - breakMapFilter "github.com/m4gshm/gollections/break/kv/predicate" - breakMapConvert "github.com/m4gshm/gollections/break/map_/convert" - "github.com/m4gshm/gollections/kv/convert" - kvPredicate "github.com/m4gshm/gollections/kv/predicate" -) - -// Loop is a function that returns the next key\value or ok==false if there are no more elements. -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.Seq2] -type Loop[K, V any] func() (key K, value V, ok bool) - -// All is used to iterate through the loop using `for ... range`. -func (next Loop[K, V]) All(consumer func(key K, value V) bool) { - All(next, consumer) -} - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. -func (next Loop[K, V]) Track(consumer func(K, V) error) error { - return Track(next, consumer) -} - -// First returns the first element that satisfies the condition of the 'predicate' function. -func (next Loop[K, V]) First(predicate func(K, V) bool) (K, V, bool) { - return First(next, predicate) -} - -// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -func (next Loop[K, V]) Reduce(merge func(K, K, V, V) (K, V)) (K, V, bool) { - return ReduceOK(next, merge) -} - -// Reducee reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -func (next Loop[K, V]) Reducee(merge func(K, K, V, V) (K, V, error)) (K, V, bool, error) { - return ReduceeOK(next, merge) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (next Loop[K, V]) HasAny(predicate func(K, V) bool) bool { - return HasAny(next, predicate) -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[K, V]) Filt(filter func(K, V) (bool, error)) breakkvloop.Loop[K, V] { - return Filt(next, filter) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[K, V]) Filter(filter func(K, V) bool) Loop[K, V] { - return Filter(next, filter) -} - -// Convert creates a loop that applies the 'converter' function to iterable key\values. -func (next Loop[K, V]) Convert(converter func(K, V) (K, V)) Loop[K, V] { - return Convert(next, converter) -} - -// Conv creates a loop that applies the 'converter' function to iterable key\values. -func (next Loop[K, V]) Conv(converter func(K, V) (K, V, error)) breakkvloop.Loop[K, V] { - return Conv(next, converter) -} - -// FilterKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (next Loop[K, V]) FilterKey(predicate func(K) bool) Loop[K, V] { - return Filter(next, kvPredicate.Key[V](predicate)) -} - -// FiltKey returns a loop consisting of key/value pairs where the key satisfies the condition of the 'predicate' function -func (next Loop[K, V]) FiltKey(predicate func(K) (bool, error)) breakkvloop.Loop[K, V] { - return Filt(next, breakMapFilter.Key[V](predicate)) -} - -// ConvertKey returns a loop that applies the 'converter' function to keys of the map -func (next Loop[K, V]) ConvertKey(by func(K) K) Loop[K, V] { - return Convert(next, convert.Key[V](by)) -} - -// ConvKey returns a loop that applies the 'converter' function to keys of the map -func (next Loop[K, V]) ConvKey(converter func(K) (K, error)) breakkvloop.Loop[K, V] { - return Conv(next, breakMapConvert.Key[V](converter)) -} - -// FilterValue returns a loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (next Loop[K, V]) FilterValue(predicate func(V) bool) Loop[K, V] { - return Filter(next, kvPredicate.Value[K](predicate)) -} - -// FiltValue returns a breakable loop consisting of key/value pairs where the value satisfies the condition of the 'predicate' function -func (next Loop[K, V]) FiltValue(predicate func(V) (bool, error)) breakkvloop.Loop[K, V] { - return Filt(next, breakMapFilter.Value[K](predicate)) -} - -// ConvertValue returns a loop that applies the 'converter' function to values of the map -func (next Loop[K, V]) ConvertValue(converter func(V) V) Loop[K, V] { - return Convert(next, convert.Value[K](converter)) -} - -// ConvValue returns a breakable loop that applies the 'converter' function to values of the map -func (next Loop[K, V]) ConvValue(converter func(V) (V, error)) breakkvloop.Loop[K, V] { - return Conv(next, breakMapConvert.Value[K](converter)) -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func (next Loop[K, V]) Crank() (Loop[K, V], K, V, bool) { - return Crank(next) -} diff --git a/kv/loop/test/api_test.go b/kv/loop/test/api_test.go deleted file mode 100644 index 0ea63f31..00000000 --- a/kv/loop/test/api_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package test - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" - - breakkvloop "github.com/m4gshm/gollections/break/kv/loop" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/k" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/slice" -) - -func Test_HasAny(t *testing.T) { - kvl := 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) - - result := kvloop.HasAny(kvl, func(key int, _ string) bool { return key == 2 }) - - assert.True(t, result) -} - -func Test_Firstt(t *testing.T) { - kvl := 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) - - k, v, ok, _ := kvloop.Firstt(kvl, func(key int, val string) (bool, error) { return key == 2 || val == "three", nil }) - - assert.True(t, ok) - assert.Equal(t, 2, k) - assert.Equal(t, "two", v) -} - -func Test_Reduce(t *testing.T) { - kvl := 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) - - k, v, ok := kvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.True(t, ok) - assert.Equal(t, 1+2+3, k) - assert.Equal(t, "one"+"two"+"three", v) -} - -func Test_Reduce_Empty(t *testing.T) { - kvl := loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value) - - k, v, ok := kvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.False(t, ok) - assert.Equal(t, 0, k) - assert.Equal(t, "", v) -} - -func Test_Reduce_Nil(t *testing.T) { - var kvl kvloop.Loop[int, string] - - k, v, ok := kvloop.ReduceOK(kvl, func(kl, kr int, vl, vr string) (int, string) { return kl + kr, vl + vr }) - - assert.False(t, ok) - assert.Equal(t, 0, k) - assert.Equal(t, "", v) -} - -func Test_Reducee(t *testing.T) { - kvl := 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) - - k, v, ok, _ := kvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.True(t, ok) - assert.Equal(t, 1+2+3, k) - assert.Equal(t, "one"+"two"+"three", v) -} - -func Test_Reducee_EMpty(t *testing.T) { - kvl := loop.KeyValue(loop.Of[c.KV[int, string]](), c.KV[int, string].Key, c.KV[int, string].Value) - - k, v, ok, _ := kvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.False(t, ok) - assert.Equal(t, 0, k) - assert.Equal(t, "", v) -} - -func Test_Reducee_Nil(t *testing.T) { - var kvl kvloop.Loop[int, string] - - k, v, ok, _ := kvloop.ReduceeOK(kvl, func(kl, kr int, vl, vr string) (int, string, error) { return kl + kr, vl + vr, nil }) - - assert.False(t, ok) - assert.Equal(t, 0, k) - assert.Equal(t, "", v) -} - -func Test_Convert(t *testing.T) { - kvl := 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) - - out := kvloop.Slice(kvloop.Convert(kvl, func(k int, v string) (int, string) { return k * k, v + v }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "11"), k.V(4, "22"), k.V(9, "33")), out) -} - -func Test_Conv(t *testing.T) { - kvl := 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) - - out, _ := breakkvloop.Slice(kvloop.Conv(kvl, func(k int, v string) (int, string, error) { return k * k, v + v, nil }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "11"), k.V(4, "22"), k.V(9, "33")), out) -} - -func Test_Filter(t *testing.T) { - kvl := 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) - - out := kvloop.Slice(kvloop.Filter(kvl, func(key int, _ string) bool { return key != 2 }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(3, "3")), out) -} - -func Test_Filt(t *testing.T) { - kvl := 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) - - out, _ := breakkvloop.Slice(kvloop.Filt(kvl, func(key int, _ string) (bool, error) { return key != 2, nil }), k.V[int, string]) - - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(3, "3")), out) -} - -func Test_Filt2(t *testing.T) { - kvl := 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) - - out, err := breakkvloop.Slice(kvloop.Filt(kvl, func(key int, _ string) (bool, error) { - ok := key <= 2 - return ok, op.IfElse(key == 2, errors.New("abort"), nil) - }), k.V[int, string]) - - assert.Error(t, err) - assert.Equal(t, slice.Of(k.V(1, "1")), out) -} - -func Test_NewIter(t *testing.T) { - s := slice.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3")) - i := 0 - loop := kvloop.New(s, func(s []c.KV[int, string]) bool { return i < len(s) }, func(s []c.KV[int, string]) (int, string) { - n := s[i] - i++ - return n.K, n.V - }) - - out := kvloop.Slice(loop, k.V[int, string]) - assert.Equal(t, slice.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3")), out) -} diff --git a/loop/api.go b/loop/api.go deleted file mode 100644 index bf8ce0af..00000000 --- a/loop/api.go +++ /dev/null @@ -1,924 +0,0 @@ -// Package loop provides helpers for loop operation and iterator implementations -// -// Deprecated: use the [github.com/m4gshm/gollections/seq], [github.com/m4gshm/gollections/seqe], [github.com/m4gshm/gollections/seq2] packages API instead. -package loop - -import ( - "unsafe" - - "golang.org/x/exp/constraints" - - breakkvloop "github.com/m4gshm/gollections/break/kv/loop" - breakloop "github.com/m4gshm/gollections/break/loop" - breakAlways "github.com/m4gshm/gollections/break/predicate/always" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/convert" - "github.com/m4gshm/gollections/convert/as" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/map_/resolv" - "github.com/m4gshm/gollections/notsafe" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/op/check/not" - "github.com/m4gshm/gollections/predicate/always" -) - -// Break is the 'break' statement of the For, Track methods. -var Break = c.Break - -// Continue is an alias of the nil value used to continue iterating by For, Track methods. -var Continue = c.Continue - -// S wrap the elements by loop function. -func S[TS ~[]T, T any](elements TS) Loop[T] { - return Of(elements...) -} - -// Of wrap the elements by loop function. -func Of[T any](elements ...T) Loop[T] { - l := len(elements) - if l == 0 { - return nil - } - i := 0 - return func() (T, bool) { - if i < l { - e, ok := elements[i], true - i++ - return e, ok - } - var e T - return e, false - } -} - -// All is an adapter for the next function for iterating by `for ... range`. -func All[T any](next func() (T, bool), consumer func(T) bool) { - if next == nil { - return - } - for v, ok := next(); ok && consumer(v); v, ok = next() { - } -} - -// New makes a loop from an abstract source -func New[S, T any](source S, hasNext func(S) bool, getNext func(S) T) Loop[T] { - return func() (out T, ok bool) { - if hasNext(source) { - out, ok = getNext(source), true - } - return out, ok - } -} - -// For applies the 'consumer' function for the elements retrieved by the 'next' function until the consumer returns the c.Break to stop. -func For[T any](next func() (T, bool), consumer func(T) error) error { - if next == nil { - return nil - } - for v, ok := next(); ok; v, ok = next() { - if err := consumer(v); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - -// ForEach applies the 'consumer' function to the elements retrieved by the 'next' function -func ForEach[T any](next func() (T, bool), consumer func(T)) { - if next == nil { - return - } - for v, ok := next(); ok; v, ok = next() { - consumer(v) - } -} - -// ForEachFiltered applies the 'consumer' function to the elements retrieved by the 'next' function that satisfy the 'predicate' function condition -func ForEachFiltered[T any](next func() (T, bool), predicate func(T) bool, consumer func(T)) { - if next == nil { - return - } - for v, ok := next(); ok; v, ok = next() { - if predicate(v) { - consumer(v) - } - } -} - -// First returns the first element that satisfies the condition of the 'predicate' function -func First[T any](next func() (T, bool), predicate func(T) bool) (v T, ok bool) { - if next == nil { - return v, false - } - for one, ok := next(); ok; one, ok = next() { - if predicate(one) { - return one, true - } - } - return v, ok -} - -// Firstt returns the first element that satisfies the condition of the 'predicate' function -func Firstt[T any](next func() (T, bool), predicate func(T) (bool, error)) (v T, ok bool, err error) { - if next == nil { - return v, false, nil - } - for { - if out, ok := next(); !ok { - return out, false, nil - } else if ok, err := predicate(out); err != nil || ok { - return out, ok, err - } - } -} - -// Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop.tracking. -func Track[I, T any](next func() (I, T, bool), consumer func(I, T) error) error { - return kvloop.Track(next, consumer) -} - -// TrackEach applies the 'consumer' function to position/element pairs retrieved by the 'next' function -func TrackEach[I, T any](next func() (I, T, bool), consumer func(I, T)) { - kvloop.TrackEach(next, consumer) -} - -// Slice collects the elements retrieved by the 'next' function into a new slice -func Slice[T any](next func() (T, bool)) []T { - return SliceCap(next, 0) -} - -// SliceCap collects the elements retrieved by the 'next' function into a new slice with predefined capacity -func SliceCap[T any](next func() (T, bool), capacity int) (out []T) { - if next == nil { - return nil - } - if capacity > 0 { - out = make([]T, 0, capacity) - } - return Append(next, out) -} - -// Append collects the elements retrieved by the 'next' function into the specified 'out' slice -func Append[T any, TS ~[]T](next func() (T, bool), out TS) TS { - if next == nil { - return out - } - for { - v, ok := next() - if !ok { - break - } - out = append(out, v) - } - return out -} - -// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func Reduce[T any](next func() (T, bool), merge func(T, T) T) T { - result, _ := ReduceOK(next, merge) - return result -} - -// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceOK[T any](next func() (T, bool), merge func(T, T) T) (result T, ok bool) { - if next == nil { - return result, false - } - if result, ok = next(); !ok { - return result, false - } - return Accum(result, next, merge), true -} - -// Reducee reduces the elements retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func Reducee[T any](next func() (T, bool), merge func(T, T) (T, error)) (T, error) { - result, _, err := ReduceeOK(next, merge) - return result, err -} - -// ReduceeOK reduces the elements retrieved by the 'next' function into an one pair using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func ReduceeOK[T any](next func() (T, bool), merge func(T, T) (T, error)) (result T, ok bool, err error) { - if next == nil { - return result, false, nil - } - if result, ok = next(); !ok { - return result, false, nil - } - result, err = Accumm(result, next, merge) - return result, true, err -} - -// 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. -func Accum[T any](first T, next func() (T, bool), merge func(T, T) T) T { - accumulator := first - if next == nil { - return accumulator - } - for v, ok := next(); ok; v, ok = next() { - accumulator = merge(accumulator, v) - } - return accumulator -} - -// 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. -func Accumm[T any](first T, next func() (T, bool), merge func(T, T) (T, error)) (accumulator T, err error) { - accumulator = first - if next == nil { - return accumulator, nil - } - for v, ok := next(); ok; v, ok = next() { - accumulator, err = merge(accumulator, v) - if err != nil { - return accumulator, err - } - } - return accumulator, nil -} - -// Sum returns the sum of all elements -func Sum[T c.Summable](next func() (T, bool)) (out T) { - return Accum(out, next, op.Sum[T]) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func HasAny[T any](next func() (T, bool), predicate func(T) bool) bool { - _, ok := First(next, predicate) - return ok -} - -// Contains finds the first element that equal to the example and returns true -func Contains[T comparable](next func() (T, bool), example T) bool { - if next == nil { - return false - } - for one, ok := next(); ok; one, ok = next() { - if one == example { - return true - } - } - return false -} - -// Conv creates a loop that applies the 'converter' function to iterable elements. -func Conv[From, To any](next func() (From, bool), converter func(From) (To, error)) breakloop.Loop[To] { - return breakloop.Conv(breakloop.From(next), converter) -} - -// ConvS creates a loop that applies the 'converter' function to the 'elements' slice. -func ConvS[FS ~[]From, From, To any](elements FS, converter func(From) (To, error)) breakloop.Loop[To] { - return Conv(S(elements), converter) -} - -// Convert creates a loop that applies the 'converter' function to iterable elements. -func Convert[From, To any](next func() (From, bool), converter func(From) To) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool) { - v, ok := next() - if ok { - return converter(v), true - } - return t, false - } -} - -// ConvertS creates a loop that applies the 'converter' function to the 'elements' slice. -func ConvertS[FS ~[]From, From, To any](elements FS, converter func(From) To) Loop[To] { - return Convert(S(elements), converter) -} - -// ConvOK creates a loop that applies the 'converter' function to iterable elements. -// The converter may returns a value or ok=false to exclude the value from the loop. -// It may also return an error to abort the loop. -func ConvOK[From, To any](next func() (From, bool), converter func(from From) (To, bool, error)) breakloop.Loop[To] { - return breakloop.ConvOK(breakloop.From(next), converter) -} - -// ConvertOK creates a loop that applies the 'converter' function to iterable elements. -// The converter may returns a value or ok=false to exclude the value from the loop. -func ConvertOK[From, To any](next func() (From, bool), converter func(from From) (To, bool)) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool) { - for e, ok := next(); ok; e, ok = next() { - if t, ok := converter(e); ok { - return t, true - } - } - return t, false - } -} - -// FiltAndConv creates a loop that filters source elements and converts them -func FiltAndConv[From, To any](next func() (From, bool), filter func(From) (bool, error), converter func(From) (To, error)) breakloop.Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool, err error) { - for { - if f, ok, err := Firstt(next, filter); err != nil || !ok { - return t, false, err - } else if cf, err := converter(f); err != nil { - return t, false, err - } else { - return cf, true, nil - } - } - } -} - -// FilterAndConvert creates a loop that filters source elements and converts them -func FilterAndConvert[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To) Loop[To] { - return FilterConvertFilter(next, filter, converter, always.True[To]) -} - -// FilterConvertFilter filters source, converts, and filters converted elements -func FilterConvertFilter[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To, filterTo func(To) bool) Loop[To] { - if next == nil { - return nil - } - return func() (t To, ok bool) { - for { - if f, ok := First(next, filter); !ok { - return t, false - } else if t = converter(f); filterTo(t) { - return t, ok - } - } - } -} - -// ConvertAndFilter additionally filters 'To' elements -func ConvertAndFilter[From, To any](next func() (From, bool), converter func(From) To, filter func(To) bool) Loop[To] { - return FilterConvertFilter(next, always.True[From], converter, filter) -} - -// Flatt converts a two-dimensional loop in an one-dimensional one. -func Flatt[From, To any](next func() (From, bool), flattener func(From) ([]To, error)) breakloop.Loop[To] { - return breakloop.Flatt(breakloop.From(next), flattener) -} - -// FlattS transforms the n-dimensional 'elements' slice to a n-1 dimensional loop. -func FlattS[FS ~[]From, From, To any](elements FS, flattener func(From) ([]To, error)) breakloop.Loop[To] { - return Flatt(S(elements), flattener) -} - -// Flat converts a two-dimensional loop in a one-dimensional one, like: -// -// var arrays func() ([]int, boot) = ... -// var ints func() (int, boot) = loop.Flat(arrays, as.Is) -func Flat[From, To any](next func() (From, bool), flattener func(From) []To) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool) { - if sizeTo > 0 { - if indexTo < sizeTo { - i := indexTo - indexTo++ - return *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)), true - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - for { - if v, ok := next(); !ok { - var no To - return no, false - } else if elementsTo := flattener(v); len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - return *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)), true - } - } - } -} - -// FlatS creates a loop that extracts slices of 'To' by the 'flattener' function from the elements of 'From' and flattens as one iterable collection of 'To' elements. -func FlatS[FS ~[]From, From, To any](elements FS, flattener func(From) []To) Loop[To] { - return Flat(S(elements), flattener) -} - -// FiltAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function -func FiltAndFlat[From, To any](next func() (From, bool), filter func(From) (bool, error), flattener func(From) ([]To, error)) breakloop.Loop[To] { - return breakloop.FiltFlattFilt(breakloop.From(next), filter, flattener, breakAlways.True[To]) -} - -// FilterAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function -func FilterAndFlat[From, To any](next func() (From, bool), filter func(From) bool, flattener func(From) []To) Loop[To] { - return FilterFlatFilter(next, filter, flattener, always.True[To]) -} - -// FlatAndFilt extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FlatAndFilt[From, To any](next func() (From, bool, error), flattener func(From) ([]To, error), filterTo func(To) (bool, error)) breakloop.Loop[To] { - return breakloop.FiltFlattFilt(next, breakAlways.True[From], flattener, filterTo) -} - -// FlattAndFilter extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FlattAndFilter[From, To any](next func() (From, bool), flattener func(From) []To, filterTo func(To) bool) Loop[To] { - return FilterFlatFilter(next, always.True[From], flattener, filterTo) -} - -// FiltFlattFilt filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FiltFlattFilt[From, To any](next func() (From, bool), filterFrom func(From) (bool, error), flattener func(From) ([]To, error), filterTo func(To) (bool, error)) breakloop.Loop[To] { - return breakloop.FiltFlattFilt(breakloop.From(next), filterFrom, flattener, filterTo) -} - -// FilterFlatFilter filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements -func FilterFlatFilter[From, To any](next func() (From, bool), filterFrom func(From) bool, flattener func(From) []To, filterTo func(To) bool) Loop[To] { - if next == nil { - return nil - } - var ( - elemSizeTo uintptr = notsafe.GetTypeSize[To]() - arrayTo unsafe.Pointer - indexTo, sizeTo int - ) - return func() (t To, ok bool) { - for { - if sizeTo > 0 { - if indexTo < sizeTo { - i := indexTo - indexTo++ - t = *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)) - if ok := filterTo(t); ok { - return t, true - } - } - indexTo = 0 - arrayTo = nil - sizeTo = 0 - } - - if v, ok := next(); !ok { - return t, false - } else if filterFrom(v) { - if elementsTo := flattener(v); len(elementsTo) > 0 { - indexTo = 1 - header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) - arrayTo = unsafe.Pointer(header.Data) - sizeTo = header.Len - t = *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)) - if ok := filterTo(t); ok { - return t, true - } - } - } - } - } -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filt[T any](next func() (T, bool), filter func(T) (bool, error)) breakloop.Loop[T] { - return breakloop.Filt(breakloop.From(next), filter) -} - -// FiltS creates a loop that checks slice elements by the 'filter' function and returns successful ones. -func FiltS[TS ~[]T, T any](elements TS, filter func(T) (bool, error)) breakloop.Loop[T] { - return Filt(S(elements), filter) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func Filter[T any](next func() (T, bool), filter func(T) bool) Loop[T] { - if next == nil { - return nil - } - return func() (T, bool) { - return First(next, filter) - } -} - -// FilterS creates a loop that checks slice elements by the 'filter' function and returns successful ones. -func FilterS[TS ~[]T, T any](elements TS, filter func(T) bool) Loop[T] { - return Filter(S(elements), filter) -} - -// NotNil creates a loop that filters nullable elements -func NotNil[T any](next func() (*T, bool)) Loop[*T] { - return Filter(next, not.Nil[T]) -} - -// PtrVal creates a loop that transform pointers to the values referenced by those pointers. -// Nil pointers are transformet to zero values. -func PtrVal[T any](next func() (*T, bool)) Loop[T] { - return Convert(next, convert.PtrVal[T]) -} - -// NoNilPtrVal creates a loop that transform only not nil pointers to the values referenced referenced by those pointers. -// Nil pointers are ignored. -func NoNilPtrVal[T any](next func() (*T, bool)) Loop[T] { - return ConvertOK(next, convert.NoNilPtrVal[T]) -} - -// KeyValue transforms a loop to the key/value loop based on applying key, value extractors to the elements -func KeyValue[T any, K, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) kvloop.Loop[K, V] { - if next == nil { - return nil - } - return func() (key K, value V, ok bool) { - if elem, nextOk := next(); nextOk { - key = keyExtractor(elem) - value = valExtractor(elem) - ok = true - } - return key, value, ok - } -} - -// KeyValuee transforms a loop to the key/value loop based on applying key, value extractors to the elements -func KeyValuee[T any, K, V any](next func() (T, bool), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) breakkvloop.Loop[K, V] { - return breakloop.KeyValuee(breakloop.From(next), keyExtractor, valExtractor) -} - -// KeysValues transforms a loop to the key/value loop based on applying multiple keys, values extractor to the elements -func KeysValues[T, K, V any](next func() (T, bool), keysExtractor func(T) []K, valsExtractor func(T) []V) kvloop.Loop[K, V] { - if next == nil { - return nil - } - var ( - keys []K - values []V - ki, vi int - ) - return func() (key K, value V, ok bool) { - for !ok { - var ( - keysLen, valuesLen = len(keys), len(values) - lastKeyIndex, lastValIndex = keysLen - 1, valuesLen - 1 - ) - if keysLen > 0 && ki >= 0 && ki <= lastKeyIndex { - key = keys[ki] - ok = true - } - if valuesLen > 0 && vi >= 0 && vi <= lastValIndex { - value = values[vi] - ok = true - } - if ok { - if ki < lastKeyIndex { - ki++ - } else if vi < lastValIndex { - ki = 0 - vi++ - } else { - keys, values = nil, nil - } - } else if elem, nextOk := next(); nextOk { - keys = keysExtractor(elem) - values = valsExtractor(elem) - ki, vi = 0, 0 - } else { - keys, values = nil, nil - break - } - } - return key, value, ok - } -} - -// KeysValue transforms a loop to the key/value loop based on applying keys, value extractor to the elements -func KeysValue[T, K, V any](next func() (T, bool), keysExtractor func(T) []K, valExtractor func(T) V) kvloop.Loop[K, V] { - return KeysValues(next, keysExtractor, func(t T) []V { return convert.AsSlice(valExtractor(t)) }) -} - -// KeysValuee transforms a loop to the key/value loop based on applying keys, value extractor to the elements -func KeysValuee[T, K, V any](next func() (T, bool), keysExtractor func(T) ([]K, error), valExtractor func(T) (V, error)) breakkvloop.Loop[K, V] { - return breakloop.KeysValuee(breakloop.From(next), keysExtractor, valExtractor) -} - -// KeyValues transforms a loop to the key/value loop based on applying key, values extractor to the elements -func KeyValues[T, K, V any](next func() (T, bool), keyExtractor func(T) K, valsExtractor func(T) []V) kvloop.Loop[K, V] { - return KeysValues(next, func(t T) []K { return convert.AsSlice(keyExtractor(t)) }, valsExtractor) -} - -// KeyValuess transforms a loop to the key/value loop based on applying key, values extractor to the elements -func KeyValuess[T, K, V any](next func() (T, bool), keyExtractor func(T) (K, error), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[K, V] { - return breakloop.KeyValuess(breakloop.From(next), keyExtractor, valsExtractor) -} - -// ExtraVals transforms a loop to the key/value loop based on applying values extractor to the elements -func ExtraVals[T, V any](next func() (T, bool), valsExtractor func(T) []V) kvloop.Loop[T, V] { - return KeyValues(next, as.Is[T], valsExtractor) -} - -// ExtraValss transforms a loop to the key/value loop based on applying values extractor to the elements -func ExtraValss[T, V any](next func() (T, bool), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[T, V] { - return KeyValuess(next, as.ErrTail(as.Is[T]), valsExtractor) -} - -// ExtraKeys transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeys[T, K any](next func() (T, bool), keysExtractor func(T) []K) kvloop.Loop[K, T] { - return KeysValue(next, keysExtractor, as.Is[T]) -} - -// ExtraKeyss transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeyss[T, K any](next func() (T, bool), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { - return KeyValuess(next, keyExtractor, as.ErrTail(convert.AsSlice[T])) -} - -// ExtraKey transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKey[T, K any](next func() (T, bool), keysExtractor func(T) K) kvloop.Loop[K, T] { - return KeyValue(next, keysExtractor, as.Is[T]) -} - -// ExtraKeyy transforms a loop to the key/value loop based on applying key extractor to the elements -func ExtraKeyy[T, K any](next func() (T, bool), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { - return breakloop.KeyValuee[T, K](breakloop.From(next), keyExtractor, as.ErrTail(as.Is[T])) -} - -// ExtraValue transforms a loop to the key/value loop based on applying value extractor to the elements -func ExtraValue[T, V any](next func() (T, bool), valueExtractor func(T) V) kvloop.Loop[T, V] { - return KeyValue(next, as.Is[T], valueExtractor) -} - -// ExtraValuee transforms a loop to the key/value loop based on applying value extractor to the elements -func ExtraValuee[T, V any](next func() (T, bool), valExtractor func(T) (V, error)) breakkvloop.Loop[T, V] { - return breakloop.KeyValuee[T, T, V](breakloop.From(next), as.ErrTail(as.Is[T]), valExtractor) -} - -// Group converts elements retrieved by the 'next' function into a new map, extracting a key for each element applying the converter 'keyExtractor'. -// The keyExtractor converts an element to a key. -// The valExtractor converts an element to a value. -func Group[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) map[K][]V { - return MapResolv(next, keyExtractor, valExtractor, resolv.Slice[K, V]) -} - -// Groupp converts elements retrieved by the 'next' function into a new map, extracting a key for each element applying the converter 'keyExtractor'. -// The keyExtractor converts an element to a key. -// The valExtractor converts an element to a value. -func Groupp[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) (map[K][]V, error) { - return breakloop.Groupp(breakloop.From(next), keyExtractor, valExtractor) -} - -// GroupByMultiple converts elements retrieved by the 'next' function into a new map, extracting multiple keys, values per each element applying the 'keysExtractor' and 'valsExtractor' functions. -// The keysExtractor retrieves one or more keys per element. -// The valsExtractor retrieves one or more values per element. -func GroupByMultiple[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valsExtractor func(T) []V) map[K][]V { - if next == nil { - return nil - } - groups := map[K][]V{} - for e, ok := next(); ok; e, ok = next() { - if keys, vals := keysExtractor(e), valsExtractor(e); len(keys) == 0 { - var key K - for _, v := range vals { - initGroup(key, v, groups) - } - } else { - for _, key := range keys { - if len(vals) == 0 { - var v V - initGroup(key, v, groups) - } else { - for _, v := range vals { - initGroup(key, v, groups) - } - } - } - } - } - return groups -} - -// GroupByMultipleKeys converts elements retrieved by the 'next' function into a new map, extracting multiple keys, one value per each element applying the 'keysExtractor' and 'valExtractor' functions. -// The keysExtractor retrieves one or more keys per element. -// The valExtractor converts an element to a value. -func GroupByMultipleKeys[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valExtractor func(T) V) map[K][]V { - if next == nil { - return nil - } - groups := map[K][]V{} - for e, ok := next(); ok; e, ok = next() { - if keys, v := keysExtractor(e), valExtractor(e); len(keys) == 0 { - var key K - initGroup(key, v, groups) - } else { - for _, key := range keys { - initGroup(key, v, groups) - } - } - } - return groups -} - -// GroupByMultipleValues converts elements retrieved by the 'next' function into a new map, extracting one key, multiple values per each element applying the 'keyExtractor' and 'valsExtractor' functions. -// The keyExtractor converts an element to a key. -// The valsExtractor retrieves one or more values per element. -func GroupByMultipleValues[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valsExtractor func(T) []V) map[K][]V { - if next == nil { - return nil - } - groups := map[K][]V{} - for e, ok := next(); ok; e, ok = next() { - if key, vals := keyExtractor(e), valsExtractor(e); len(vals) == 0 { - var v V - initGroup(key, v, groups) - } else { - for _, v := range vals { - initGroup(key, v, groups) - } - } - } - return groups -} - -func initGroup[T any, K comparable, TS ~[]T](key K, e T, groups map[K]TS) { - groups[key] = append(groups[key], e) -} - -// Map collects key\value elements into a new map by iterating over the elements -func Map[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) map[K]V { - return MapResolv(next, keyExtractor, valExtractor, resolv.First[K, V]) -} - -// Mapp collects key\value elements into a new map by iterating over the elements -func Mapp[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) (map[K]V, error) { - return breakloop.Mapp(breakloop.From(next), keyExtractor, valExtractor) -} - -// MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values -func MapResolv[T any, K comparable, V, VR any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR) map[K]VR { - return AppendMapResolv(next, keyExtractor, valExtractor, resolver, nil) -} - -// MapResolvOrder collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values. -// Returns a slice with the keys ordered by the time they were added and the resolved key\value map. -func MapResolvOrder[TS ~[]T, T any, K comparable, V, VR any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR) ([]K, map[K]VR) { - return AppendMapResolvOrder(next, keyExtractor, valExtractor, resolver, nil, nil) -} - -// AppendMapResolv collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values -func AppendMapResolv[T any, K comparable, V, VR any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR, dest map[K]VR) map[K]VR { - if next == nil { - return nil - } - if dest == nil { - dest = map[K]VR{} - } - for e, ok := next(); ok; e, ok = next() { - k, v := keyExtractor(e), valExtractor(e) - exists, ok := dest[k] - dest[k] = resolver(ok, k, exists, v) - } - return dest -} - -// AppendMapResolvOrder collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values -// Additionaly populates the 'order' slice by the keys ordered by the time they were added and the resolved key\value map. -func AppendMapResolvOrder[T any, K comparable, V, VR any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR, order []K, dest map[K]VR) ([]K, map[K]VR) { - if next == nil { - return nil, nil - } - if dest == nil { - dest = map[K]VR{} - } - for e, ok := next(); ok; e, ok = next() { - k, v := keyExtractor(e), valExtractor(e) - exists, ok := dest[k] - dest[k] = resolver(ok, k, exists, v) - if !ok { - order = append(order, k) - } - } - return order, dest -} - -// Series makes a sequence by applying the 'next' function to the previous step generated value. -func Series[T any](first T, next func(T) (T, bool)) Loop[T] { - if next == nil { - return nil - } - current := first - init := true - return func() (out T, ok bool) { - if init { - init = false - return current, true - } else { - next, ok := next(current) - current = next - return current, ok - } - } -} - -// RangeClosed creates a loop that generates integers in the range defined by from and to inclusive -func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) Loop[T] { - amount := toInclusive - from - delta := T(1) - if amount < 0 { - amount = -amount - delta = -delta - } - amount++ - nextElement := from - i := T(0) - return func() (out T, ok bool) { - if ok = i < amount; ok { - out = nextElement - i++ - nextElement = nextElement + delta - } - return out, ok - } -} - -// Range creates a loop that generates integers in the range defined by from and to exclusive -func Range[T constraints.Integer | rune](from T, toExclusive T) Loop[T] { - amount := toExclusive - from - delta := T(1) - if amount < 0 { - amount = -amount - delta = -delta - } - nextElement := from - i := T(0) - return func() (out T, ok bool) { - if ok = i < amount; ok { - out = nextElement - i++ - nextElement = nextElement + delta - } - return out, ok - } -} - -// OfIndexed builds a loop by extracting elements from an indexed soruce. -// the len is length ot the source. -// the getAt retrieves an element by its index from the source. -func OfIndexed[T any](amount int, getAt func(int) T) Loop[T] { - if getAt == nil { - return nil - } - i := 0 - return func() (out T, ok bool) { - if ok = i < amount; ok { - out = getAt(i) - i++ - } - return out, ok - } -} - -// ConvertAndReduce converts each elements and merges them into one -func ConvertAndReduce[From, To any](next func() (From, bool), converter func(From) To, merge func(To, To) To) (out To) { - if next == nil { - return out - } - if v, ok := next(); ok { - out = converter(v) - } else { - return out - } - for v, ok := next(); ok; v, ok = next() { - out = merge(out, converter(v)) - } - return out -} - -// ConvAndReduce converts each elements and merges them into one -func ConvAndReduce[From, To any](next func() (From, bool), converter func(From) (To, error), merge func(To, To) To) (out To, err error) { - if next == nil { - return out, nil - } - if v, ok := next(); ok { - out, err = converter(v) - if err != nil { - return out, err - } - } else { - return out, nil - } - for v, ok := next(); ok; v, ok = next() { - c, err := converter(v) - if err != nil { - return out, err - } - out = merge(out, c) - } - return out, nil -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func Crank[T any](next func() (T, bool)) (n Loop[T], t T, ok bool) { - if next != nil { - t, ok = next() - } - return next, t, ok -} diff --git a/loop/conv/api.go b/loop/conv/api.go deleted file mode 100644 index 2820dcd9..00000000 --- a/loop/conv/api.go +++ /dev/null @@ -1,17 +0,0 @@ -// Package conv provides loop converation helpers -package conv - -import ( - breakLoop "github.com/m4gshm/gollections/break/loop" - "github.com/m4gshm/gollections/loop" -) - -// FromIndexed - conv.FromIndexed retrieves elements from a indexed source and converts them -func FromIndexed[From, To any](amount int, next func(int) From, converter func(from From) (To, error)) breakLoop.Loop[To] { - return loop.Conv(loop.OfIndexed(amount, next), converter) -} - -// AndReduce - convert.AndReduce converts elements and merge them into one -func AndReduce[From, To any](next func() (From, bool), converter func(From) (To, error), merge func(To, To) To) (To, error) { - return loop.ConvAndReduce(next, converter, merge) -} diff --git a/loop/convert/api.go b/loop/convert/api.go deleted file mode 100644 index c687da31..00000000 --- a/loop/convert/api.go +++ /dev/null @@ -1,59 +0,0 @@ -// Package convert provides loop converation helpers -package convert - -import ( - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op/check/not" -) - -// AndConvert - convert.AndConvert makes double converts From->Intermediate->To of the elements -func AndConvert[From, I, To any](next func() (From, bool), firsConverter func(From) I, secondConverter func(I) To) loop.Loop[To] { - return loop.Convert(next, func(from From) To { return secondConverter(firsConverter(from)) }) -} - -// AndFilter - convert.AndFilter converts only filtered elements and returns them -func AndFilter[From, To any](next func() (From, bool), converter func(From) To, filter func(To) bool) loop.Loop[To] { - return loop.ConvertAndFilter(next, converter, filter) -} - -// NotNil - convert.NotNil converts only not nil elements and returns them -func NotNil[From, To any](next func() (*From, bool), converter func(*From) To) loop.Loop[To] { - return loop.FilterAndConvert(next, not.Nil[From], converter) -} - -// ToNotNil - convert.ToNotNil converts elements and returns only not nil converted elements -func ToNotNil[From, To any](next func() (From, bool), converter func(From) *To) loop.Loop[*To] { - return loop.ConvertOK(next, func(f From) (*To, bool) { - if t := converter(f); t != nil { - return t, true - } - return nil, false - }) -} - -// NilSafe - convert.NilSafe filters not nil next, converts that ones, filters not nils after converting and returns them -func NilSafe[From, To any](next func() (*From, bool), converter func(*From) *To) loop.Loop[*To] { - return loop.ConvertOK(next, func(f *From) (*To, bool) { - if f != nil { - if t := converter(f); t != nil { - return t, true - } - } - return nil, false - }) -} - -// Check - convert.Check is a short alias of loop.ConvertOK -func Check[From, To any](next func() (From, bool), converter func(from From) (To, bool)) loop.Loop[To] { - return loop.ConvertOK(next, converter) -} - -// FromIndexed - convert.FromIndexed retrieves elements from a indexed source and converts them -func FromIndexed[From, To any](amount int, next func(int) From, converter func(from From) To) loop.Loop[To] { - return loop.Convert(loop.OfIndexed(amount, next), converter) -} - -// AndReduce - convert.AndReduce converts elements and merge them into one -func AndReduce[From, To any](next func() (From, bool), converter func(From) To, merge func(To, To) To) (out To) { - return loop.ConvertAndReduce(next, converter, merge) -} diff --git a/loop/filter/api.go b/loop/filter/api.go deleted file mode 100644 index fd33e0e5..00000000 --- a/loop/filter/api.go +++ /dev/null @@ -1,16 +0,0 @@ -// Package filter provides aliases for loop filtering helpers -package filter - -import ( - "github.com/m4gshm/gollections/loop" -) - -// AndConvert filters the 'From' elements, and then converts them to 'To' -func AndConvert[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To) loop.Loop[To] { - return loop.FilterAndConvert(next, filter, converter) -} - -// ConvertFilter filters the 'From' elements, then converts them to 'To', and then filters that ones -func ConvertFilter[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To, filterTo func(To) bool) loop.Loop[To] { - return loop.FilterConvertFilter(next, filter, converter, filterTo) -} diff --git a/loop/first/api.go b/loop/first/api.go deleted file mode 100644 index 2b40b8c7..00000000 --- a/loop/first/api.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package first provides short aliases for loop functions for retrieving a first element -package first - -import ( - "github.com/m4gshm/gollections/loop" -) - -// Of an alias of the loop.First -func Of[T any](next func() (T, bool), predicate func(T) bool) (T, bool) { - return loop.First(next, predicate) -} - -// Converted converts the first element that satisfies the condition of the 'predicate' function by the converter and returns it -func Converted[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To) (out To, ok bool) { - if f, ok := loop.First(next, filter); ok { - return converter(f), true - } - return out, false -} diff --git a/loop/flat/api.go b/loop/flat/api.go deleted file mode 100644 index 9e36bd32..00000000 --- a/loop/flat/api.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package flat provides short aliases for loop functions -package flat - -import "github.com/m4gshm/gollections/loop" - -// AndConvert - flattener.AndConvert flattens and converts elements retrieved by the 'next' function -func AndConvert[From, I, To any](next func() (From, bool), flattener func(From) []I, convert func(I) To) loop.Loop[To] { - return loop.Convert(loop.Flat(next, flattener), convert) -} diff --git a/loop/group/api.go b/loop/group/api.go deleted file mode 100644 index ad1e78c6..00000000 --- a/loop/group/api.go +++ /dev/null @@ -1,26 +0,0 @@ -// Package group provides short aliases for functions that are used to group elements retrieved by a loop -package group - -import ( - "github.com/m4gshm/gollections/loop" -) - -// Of is a short alias for loop.Group -func Of[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) map[K][]V { - return loop.Group(next, keyExtractor, valExtractor) -} - -// ByMultiple is a short alias for loop.GroupByMultiple -func ByMultiple[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valsExtractor func(T) []V) map[K][]V { - return loop.GroupByMultiple(next, keysExtractor, valsExtractor) -} - -// ByMultipleKeys is a short alias for loop.GroupByMultipleKeys -func ByMultipleKeys[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valExtractor func(T) V) map[K][]V { - return loop.GroupByMultipleKeys(next, keysExtractor, valExtractor) -} - -// ByMultipleValues is a short alias for loop.GroupByMultipleVals -func ByMultipleValues[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valsExtractor func(T) []V) map[K][]V { - return loop.GroupByMultipleValues(next, keyExtractor, valsExtractor) -} diff --git a/loop/loop.go b/loop/loop.go deleted file mode 100644 index cad25a89..00000000 --- a/loop/loop.go +++ /dev/null @@ -1,119 +0,0 @@ -package loop - -import ( - "github.com/m4gshm/gollections/break/loop" - "github.com/m4gshm/gollections/c" -) - -// Loop is a function that returns the next element or ok=false if there are no more elements. -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.Seq] -type Loop[T any] func() (element T, ok bool) - -var ( - _ c.Filterable[any, Loop[any], loop.Loop[any]] = (Loop[any])(nil) - _ c.Convertable[any, Loop[any], loop.Loop[any]] = (Loop[any])(nil) -) - -// All is used to iterate through the loop using `for ... range`. -func (next Loop[T]) All(consumer func(T) bool) { - All(next, consumer) -} - -// For applies the 'consumer' function for the elements retrieved by the 'next' function until the consumer returns the c.Break to stop. -func (next Loop[T]) For(consumer func(T) error) error { - return For(next, consumer) -} - -// ForEach applies the 'consumer' function to the elements retrieved by the 'next' function -func (next Loop[T]) ForEach(consumer func(T)) { - ForEach(next, consumer) -} - -// ForEachFiltered applies the 'consumer' function to the elements retrieved by the 'next' function that satisfy the 'predicate' function condition -func (next Loop[T]) ForEachFiltered(predicate func(T) bool, consumer func(T)) { - ForEachFiltered(next, predicate, consumer) -} - -// First returns the first element that satisfies the condition of the 'predicate' function -func (next Loop[T]) First(predicate func(T) bool) (T, bool) { - return First(next, predicate) -} - -// Slice collects the elements retrieved by the 'next' function into a new slice -func (next Loop[T]) Slice() []T { - return Slice(next) -} - -// SliceCap collects the elements retrieved by the 'next' function into a new slice with predefined capacity -func (next Loop[T]) SliceCap(capacity int) []T { - return SliceCap(next, capacity) -} - -// Append collects the elements retrieved by the 'next' function into the specified 'out' slice -func (next Loop[T]) Append(out []T) []T { - return Append(next, out) -} - -// Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func (next Loop[T]) Reduce(merge func(T, T) T) T { - return Reduce(next, merge) -} - -// ReduceOK reduces the elements retrieved by the 'next' function into an one using the 'merge' function. -// Returns ok==false if the 'next' function returns ok=false at the first call (no more elements). -func (next Loop[T]) ReduceOK(merge func(T, T) T) (result T, ok bool) { - return ReduceOK(next, merge) -} - -// Reducee reduces the elements retrieved by the 'next' function into an one pair using the 'merge' function. -// If the 'next' function returns ok=false at the first call, the zero value of 'T' type is returned. -func (next Loop[T]) Reducee(merge func(T, T) (T, error)) (T, error) { - return Reducee(next, merge) -} - -// ReduceeOK reduces the elements retrieved by the 'next' function into an one pair using the 'merge' function. -func (next Loop[T]) ReduceeOK(merge func(T, T) (T, error)) (resul T, ok bool, err error) { - return ReduceeOK(next, merge) -} - -// 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. -func (next Loop[T]) Accum(first T, merge func(T, T) T) T { - return Accum(first, next, merge) -} - -// 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. -func (next Loop[T]) Accumm(first T, merge func(T, T) (T, error)) (T, error) { - return Accumm(first, next, merge) -} - -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful -func (next Loop[T]) HasAny(predicate func(T) bool) bool { - return HasAny(next, predicate) -} - -// Filt creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[T]) Filt(filter func(T) (bool, error)) loop.Loop[T] { - return Filt(next, filter) -} - -// Filter creates a loop that checks elements by the 'filter' function and returns successful ones. -func (next Loop[T]) Filter(filter func(T) bool) Loop[T] { - return Filter(next, filter) -} - -// Convert creates a loop that applies the 'converter' function to iterable elements. -func (next Loop[T]) Convert(converter func(T) T) Loop[T] { - return Convert(next, converter) -} - -// Conv creates a loop that applies the 'converter' function to iterable elements. -func (next Loop[T]) Conv(converter func(T) (T, error)) loop.Loop[T] { - return Conv(next, converter) -} - -// Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. -func (next Loop[T]) Crank() (Loop[T], T, bool) { - return Crank(next) -} diff --git a/loop/range_/api.go b/loop/range_/api.go deleted file mode 100644 index c6d6216d..00000000 --- a/loop/range_/api.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package range_ provides alias for the slice.Range function -package range_ - -import ( - "golang.org/x/exp/constraints" - - "github.com/m4gshm/gollections/loop" -) - -// Of - range_.Of short alias of the loop.Range -func Of[T constraints.Integer](from T, to T) loop.Loop[T] { - return loop.Range(from, to) -} - -// Closed - range_.Closed short alias of the loop.RangeClosed -func Closed[T constraints.Integer](from T, to T) loop.Loop[T] { - return loop.RangeClosed(from, to) -} diff --git a/loop/sum/api.go b/loop/sum/api.go deleted file mode 100644 index 03297087..00000000 --- a/loop/sum/api.go +++ /dev/null @@ -1,12 +0,0 @@ -// Package sum provides sum.Of alias -package sum - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/loop" -) - -// Of an alias of the loop.Sum -func Of[T c.Summable](sum func() (T, bool)) T { - return loop.Sum(sum) -} diff --git a/loop/test/api_go_1_22_test.go b/loop/test/api_go_1_22_test.go deleted file mode 100644 index 7027026b..00000000 --- a/loop/test/api_go_1_22_test.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build goexperiment.rangefunc - -package test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/loop" -) - -func Test_IterAll(t *testing.T) { - - r := []int{} - for v := range loop.Of(1, 3, 5, 7, 9, 11).All { - r = append(r, v) - } - - assert.Equal(t, []int{1, 3, 5, 7, 9, 11}, r) -} diff --git a/loop/test/api_group_test.go b/loop/test/api_group_test.go deleted file mode 100644 index 8c124106..00000000 --- a/loop/test/api_group_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/m4gshm/gollections/convert/as" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/group" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/slice" -) - -func Test_group_odd_even(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.Of(loop.Of(1, 1, 2, 4, 3, 1), even, as.Is[int]) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} - -func Test_ByMultiple(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.ByMultiple(loop.Of(1, 1, 2, 4, 3, 1), func(i int) []bool { return slice.Of(even(i)) }, as.Slice[int]) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} - -func Test_ByMultipleEmptyKey(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.ByMultiple(loop.Of(1, 1, 2, 4, 3, 1), func(i int) []bool { return op.IfElse(even(i), slice.Of(true), nil) }, as.Slice[int]) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} - -func Test_ByMultipleEmptyVal(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.ByMultiple(loop.Of(1, 1, 2, 4, 3, 1), - func(i int) []bool { return slice.Of(even(i)) }, - func(i int) []int { return op.IfElse(even(i), nil, slice.Of(i)) }, - ) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {0, 0}}, groups) -} - -func Test_ByMultipleKeys(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.ByMultipleKeys(loop.Of(1, 1, 2, 4, 3, 1), func(i int) []bool { return slice.Of(even(i)) }, as.Is[int]) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} - -func Test_ByMultipleValues(t *testing.T) { - - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.ByMultipleValues(loop.Of(1, 1, 2, 4, 3, 1), even, as.Slice[int]) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -} diff --git a/loop/test/api_test.go b/loop/test/api_test.go deleted file mode 100644 index 1361094c..00000000 --- a/loop/test/api_test.go +++ /dev/null @@ -1,502 +0,0 @@ -package test - -import ( - "errors" - "strconv" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - breakLoop "github.com/m4gshm/gollections/break/loop" - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/convert/as" - kvloop "github.com/m4gshm/gollections/kv/loop" - kvloopgroup "github.com/m4gshm/gollections/kv/loop/group" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/loop/conv" - "github.com/m4gshm/gollections/loop/convert" - "github.com/m4gshm/gollections/loop/filter" - "github.com/m4gshm/gollections/loop/first" - "github.com/m4gshm/gollections/loop/flat" - "github.com/m4gshm/gollections/loop/range_" - "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/predicate/eq" - "github.com/m4gshm/gollections/predicate/more" - "github.com/m4gshm/gollections/slice" -) - -func Test_AccumSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r := loop.Accum(100, s, op.Sum[int]) - assert.Equal(t, 100+1+3+5+7+9+11, r) -} - -func Test_AccummSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, err := loop.Accumm(100, s, func(i1, i2 int) (int, error) { - if i2 == 11 { - return i1, errors.New("stop") - } - return i1 + i2, nil - }) - assert.Equal(t, 100+1+3+5+7+9, r) - assert.ErrorContains(t, err, "stop") -} - -func Test_ReduceSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok := loop.ReduceOK(s, op.Sum[int]) - assert.True(t, ok) - assert.Equal(t, 1+3+5+7+9+11, r) -} - -func Test_ReduceeSum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok, err := loop.ReduceeOK(s, func(i1, i2 int) (int, error) { - if i2 == 11 { - return i1, errors.New("stop") - } - return i1 + i2, nil - }) - assert.True(t, ok) - assert.Equal(t, 1+3+5+7+9, r) - assert.ErrorContains(t, err, "stop") -} - -func Test_ReduceeSumFirstErr(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok, err := loop.ReduceeOK(s, func(_, _ int) (int, error) { - return 0, errors.New("stop") - }) - assert.True(t, ok) - assert.Equal(t, 0, r) - assert.ErrorContains(t, err, "stop") -} - -func Test_ReduceeEmptyLoop(t *testing.T) { - s := loop.Of[int]() - r, ok := loop.ReduceOK(s, op.Sum[int]) - assert.False(t, ok) - assert.Equal(t, 0, r) -} - -func Test_ReduceeNilLoop(t *testing.T) { - var s loop.Loop[int] - r, ok := loop.ReduceOK(s, op.Sum[int]) - assert.False(t, ok) - assert.Equal(t, 0, r) -} - -func Test_ConvertAndReduce(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r := convert.AndReduce(s, func(i int) int { return i * i }, op.Sum[int]) - assert.Equal(t, 1+3*3+5*5+7*7+9*9+11*11, r) -} - -func Test_ConvAndReduce(t *testing.T) { - s := loop.Of("1", "3", "5", "7", "9", "11") - r, err := conv.AndReduce(s, strconv.Atoi, op.Sum[int]) - assert.NoError(t, err) - assert.Equal(t, 1+3+5+7+9+11, r) -} - -func Test_Sum(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r := loop.Sum(s) - assert.Equal(t, 1+3+5+7+9+11, r) -} - -func Test_First(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok := first.Of(s, func(i int) bool { return i > 5 }) - assert.True(t, ok) - assert.Equal(t, 7, r) - - _, nook := loop.First(s, func(i int) bool { return i > 12 }) - assert.False(t, nook) -} - -func Test_FirstConverted(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r, ok := first.Converted(s, func(i int) bool { return i > 5 }, strconv.Itoa) - assert.True(t, ok) - assert.Equal(t, "7", r) -} - -func Test_NotNil(t *testing.T) { - type entity struct{ val string } - var ( - source = loop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = loop.NotNil(source) - expected = []*entity{{"first"}, {"third"}, {"fifth"}} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertPointersToValues(t *testing.T) { - type entity struct{ val string } - var ( - source = loop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = loop.PtrVal(source) - expected = []entity{{"first"}, {}, {"third"}, {}, {"fifth"}} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertNotnilPointersToValues(t *testing.T) { - type entity struct{ val string } - var ( - source = loop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = loop.NoNilPtrVal(source) - expected = []entity{{"first"}, {"third"}, {"fifth"}} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_Convert(t *testing.T) { - s := loop.Of(1, 3, 5, 7, 9, 11) - r := loop.Convert(s, strconv.Itoa) - assert.Equal(t, []string{"1", "3", "5", "7", "9", "11"}, loop.Slice(r)) -} - -func Test_IterWitErr(t *testing.T) { - s := loop.Of("1", "3", "5", "7eee", "9", "11") - r := []int{} - var outErr error - for { - it := loop.Conv(s, strconv.Atoi) - i, ok, err := it() - if !ok && err == nil { - break - } - if err != nil { - outErr = err - break - } - r = append(r, i) - } - - assert.Error(t, outErr) - assert.Equal(t, []int{1, 3, 5}, r) - - s = loop.Of("1", "3", "5", "7eee", "9", "11") - r = []int{} - //ignore err - for { - it := loop.Conv(s, strconv.Atoi) - i, ok, err := it() - if !ok && err == nil { - break - } - if err == nil { - r = append(r, i) - } - } - assert.Equal(t, []int{1, 3, 5, 9, 11}, r) -} - -func Test_IterStart(t *testing.T) { - l := loop.Convert(loop.Of(1, 3, 5, 7, 9, 11), strconv.Itoa) - r := []string{} - - for { - i, ok := l() - if !ok { - break - } - r = append(r, i) - } - assert.Equal(t, []string{"1", "3", "5", "7", "9", "11"}, r) -} - -func Test_ConvertNotNil(t *testing.T) { - type entity struct{ val string } - var ( - source = loop.Of([]*entity{{"first"}, nil, {"third"}, nil, {"fifth"}}...) - result = convert.NotNil(source, func(e *entity) string { return e.val }) - expected = []string{"first", "third", "fifth"} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertToNotNil(t *testing.T) { - type entity struct{ val *string } - var ( - first = "first" - third = "third" - fifth = "fifth" - source = loop.Of([]entity{{&first}, {}, {&third}, {}, {&fifth}}...) - result = convert.ToNotNil(source, func(e entity) *string { return e.val }) - expected = []*string{&first, &third, &fifth} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -func Test_ConvertNilSafe(t *testing.T) { - type entity struct{ val *string } - var ( - first = "first" - third = "third" - fifth = "fifth" - source = loop.Of([]*entity{{&first}, {}, {&third}, nil, {&fifth}}...) - result = convert.NilSafe(source, func(e *entity) *string { return e.val }) - expected = []*string{&first, &third, &fifth} - ) - assert.Equal(t, expected, loop.Slice(result)) -} - -var even = func(v int) bool { return v%2 == 0 } - -func Test_ConvertFiltered(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := loop.FilterAndConvert(s, even, strconv.Itoa) - assert.Equal(t, []string{"4", "8"}, loop.Slice(r)) -} - -func Test_ConvOK(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := loop.ConvertOK(s, func(i int) (string, bool) { return strconv.Itoa(i), even(i) }) - assert.Equal(t, []string{"4", "8"}, loop.Slice(r)) -} - -func Test_Flat(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := loop.Flat(md, func(i []int) []int { return i }) - e := []int{1, 2, 3, 4, 5, 6} - assert.Equal(t, e, loop.Slice(f)) -} - -func Test_FlatAndConvert(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := flat.AndConvert(md, func(i []int) []int { return i }, strconv.Itoa) - e := []string{"1", "2", "3", "4", "5", "6"} - assert.Equal(t, e, loop.Slice(f)) -} - -func Test_FlatFilter(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := loop.FilterAndFlat(md, func(from []int) bool { return len(from) > 1 }, func(i []int) []int { return i }) - e := []int{1, 2, 3, 5, 6} - assert.Equal(t, e, loop.Slice(f)) -} - -func Test_FlattElemFilter(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := loop.FlattAndFilter(md, func(i []int) []int { return i }, even) - e := []int{2, 4, 6} - assert.Equal(t, e, loop.Slice(f)) -} - -func Test_FilterAndFlattFilt(t *testing.T) { - md := loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...) - f := loop.FilterFlatFilter(md, func(from []int) bool { return len(from) > 1 }, func(i []int) []int { return i }, even) - e := []int{2, 6} - assert.Equal(t, e, loop.Slice(f)) -} - -func Test_Filter(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := loop.Filter(s, even) - assert.Equal(t, slice.Of(4, 8), loop.Slice(r)) -} - -func Test_FilterConvertFilter(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := filter.ConvertFilter(s, even, func(i int) int { return i * 2 }, even) - assert.Equal(t, slice.Of(8, 16), loop.Slice(r)) -} - -func Test_Filt(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - l := loop.Filt(s, func(i int) (bool, error) { return even(i), op.IfElse(i > 7, errors.New("abort"), nil) }) - r, err := breakLoop.Slice(l) - assert.Error(t, err) - assert.Equal(t, slice.Of(4), r) -} - -func Test_Filt2(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - l := loop.Filt(s, func(i int) (bool, error) { - ok := i <= 7 - return ok && even(i), op.IfElse(ok, nil, errors.New("abort")) - }) - r, err := breakLoop.Slice(l) - assert.Error(t, err) - assert.Equal(t, slice.Of(4), r) -} - -func Test_FiltAndConv(t *testing.T) { - s := loop.Of(1, 3, 4, 5, 7, 8, 9, 11) - r := loop.FiltAndConv(s, func(v int) (bool, error) { return v%2 == 0, nil }, func(i int) (int, error) { return i * 2, nil }) - o, _ := breakLoop.Slice(r) - assert.Equal(t, slice.Of(8, 16), o) -} - -func Test_OfLoop(t *testing.T) { - stream := loop.Of(1, 2, 3) - result := loop.Slice(stream) - - assert.Equal(t, slice.Of(1, 2, 3), result) -} - -func Test_MatchAny(t *testing.T) { - elements := loop.Of(1, 2, 3, 4) - - ok := loop.HasAny(elements, eq.To(4)) - assert.True(t, ok) - - noOk := loop.HasAny(elements, more.Than(5)) - assert.False(t, noOk) -} - -type Role struct { - name string -} - -type User struct { - name string - age int - roles []Role -} - -func (u User) Name() string { return u.name } -func (u User) Age() int { return u.age } -func (u User) Roles() []Role { return u.roles } - -var users = []User{ - {name: "Bob", age: 26, roles: []Role{{"Admin"}, {"manager"}}}, - {name: "Alice", age: 35, roles: []Role{{"Manager"}}}, - {name: "Tom", age: 18}, {}, -} - -func Test_KeyValuer(t *testing.T) { - m := kvloop.Group(loop.KeyValue(loop.Of(users...), User.Name, User.Age)) - - assert.Equal(t, m["Alice"], slice.Of(35)) - assert.Equal(t, m["Bob"], slice.Of(26)) - assert.Equal(t, m["Tom"], slice.Of(18)) - - g := loop.Group(loop.Of(users...), User.Name, User.Age) - assert.Equal(t, m, g) -} - -func Test_Keyer(t *testing.T) { - m := kvloop.Group(loop.ExtraKey(loop.Of(users...), User.Name)) - - assert.Equal(t, m["Alice"], slice.Of(users[1])) - assert.Equal(t, m["Bob"], slice.Of(users[0])) - assert.Equal(t, m["Tom"], slice.Of(users[2])) - - g := loop.Group(loop.Of(users...), User.Name, as.Is) - assert.Equal(t, m, g) -} - -func Test_Valuer(t *testing.T) { - bob, bobRoles, _ := loop.ExtraValue(loop.Of(users...), User.Roles)() - - assert.Equal(t, bob, users[0]) - assert.Equal(t, bobRoles, users[0].roles) -} - -func Test_MultiValuer(t *testing.T) { - l := loop.ExtraVals(loop.Of(users...), User.Roles) - bob, bobRole, _ := l() - bob2, bobRole2, _ := l() - - assert.Equal(t, bob, users[0]) - assert.Equal(t, bob2, users[0]) - assert.Equal(t, bobRole, users[0].roles[0]) - assert.Equal(t, bobRole2, users[0].roles[1]) -} - -func Test_MultipleKeyValuer(t *testing.T) { - m := kvloop.Group(loop.KeysValues(loop.Of(users...), - func(u User) []string { - return slice.Convert(u.roles, func(r Role) string { return strings.ToLower(r.name) }) - }, - func(u User) []string { return []string{u.name, strings.ToLower(u.name)} }, - )) - - assert.Equal(t, m["admin"], slice.Of("Bob", "bob")) - assert.Equal(t, m["manager"], slice.Of("Bob", "bob", "Alice", "alice")) - assert.Equal(t, m[""], slice.Of("Tom", "tom", "", "")) -} - -func Test_Range(t *testing.T) { - assert.Equal(t, slice.Of(-1, 0, 1, 2, 3), loop.Slice(range_.Of(-1, 4))) - assert.Equal(t, slice.Of(3, 2, 1, 0, -1), loop.Slice(range_.Of(3, -2))) - assert.Nil(t, loop.Slice(range_.Of(1, 1))) -} - -func Test_RangeClosed(t *testing.T) { - assert.Equal(t, slice.Of(-1, 0, 1, 2, 3), loop.Slice(range_.Closed(-1, 3))) - assert.Equal(t, slice.Of(3, 2, 1, 0, -1), loop.Slice(range_.Closed(3, -1))) - assert.Equal(t, slice.Of(1), loop.Slice(range_.Closed(1, 1))) -} - -func Test_Series(t *testing.T) { - assert.Equal(t, slice.Of(-1, 0, 1, 2, 3), loop.Slice(loop.Series(-1, func(prev int) (int, bool) { return prev + 1, prev < 3 }))) -} - -func Test_OfIndexed(t *testing.T) { - indexed := slice.Of("0", "1", "2", "3", "4") - result := loop.Slice(loop.OfIndexed(len(indexed), func(i int) string { return indexed[i] })) - assert.Equal(t, indexed, result) -} - -func Test_ConvertIndexed(t *testing.T) { - indexed := slice.Of(10, 11, 12, 13, 14) - result := loop.Slice(convert.FromIndexed(len(indexed), func(i int) int { return indexed[i] }, strconv.Itoa)) - assert.Equal(t, slice.Of("10", "11", "12", "13", "14"), result) -} - -func Test_ConvIndexed(t *testing.T) { - indexed := slice.Of("10", "11", "12", "13", "14") - result, err := breakLoop.Slice(conv.FromIndexed(len(indexed), func(i int) string { return indexed[i] }, strconv.Atoi)) - assert.NoError(t, err) - assert.Equal(t, slice.Of(10, 11, 12, 13, 14), result) -} - -func Test_Contains(t *testing.T) { - assert.True(t, loop.Contains(loop.Of(1, 2, 3), 3)) - assert.False(t, loop.Contains(loop.Of(1, 2, 3), 0)) -} - -func Test_New(t *testing.T) { - source := []string{"one", "two", "three"} - i := 0 - l := loop.New(source, func(s []string) bool { return i < len(s) }, func(s []string) string { o := s[i]; i++; return o }) - - assert.Equal(t, source, loop.Slice(l)) -} - -func Test_For(t *testing.T) { - var out []int - err := loop.For(loop.Of(1, 2, 3, 4), func(i int) error { - if i == 3 { - return c.Break - } - out = append(out, i) - return nil - }) - assert.NoError(t, err) - assert.Equal(t, slice.Of(1, 2), out) -} - -func Test_ForEachFiltered(t *testing.T) { - var out []int - loop.ForEachFiltered(loop.Of(1, 2, 3, 4), even, func(i int) { out = append(out, i) }) - assert.Equal(t, slice.Of(2, 4), out) -} - -func Test_FlatValues(t *testing.T) { - g := kvloopgroup.Of(loop.KeyValues(loop.Of(users...), func(u User) string { return u.name }, func(u User) []int { return slice.Of(u.age) })) - - assert.Equal(t, g["Bob"], slice.Of(26)) -} - -func Test_FlatKeys(t *testing.T) { - g := kvloopgroup.Of(loop.KeysValue(loop.Of(users...), func(u User) []string { return slice.Of(u.name) }, func(u User) int { return u.age })) - assert.Equal(t, g["Alice"], slice.Of(35)) -} diff --git a/map_/api.go b/map_/api.go index fe900173..2b81b718 100644 --- a/map_/api.go +++ b/map_/api.go @@ -10,9 +10,6 @@ import ( "github.com/m4gshm/gollections/map_/resolv" ) -// Break is For, Track breaker -var Break = c.Break - // Of instantiates a ap from the specified key/value pairs func Of[K comparable, V any](elements ...c.KV[K, V]) map[K]V { var ( @@ -26,45 +23,6 @@ func Of[K comparable, V any](elements ...c.KV[K, V]) map[K]V { return uniques } -// OfLoop builds a map by iterating key\value pairs of a source. -// The hasNext specifies a predicate that tests existing of a next pair in the source. -// The getNext extracts the pair. -// -// Deprecated: will be deleted in a next version. -func OfLoop[S any, K comparable, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, error)) (map[K]V, error) { - return OfLoopResolv(source, hasNext, getNext, resolv.First[K, V]) -} - -// OfLoopResolv builds a map by iterating elements of a source. -// The hasNext specifies a predicate that tests existing of a next pair in the source. -// The getNext extracts the element. -// The resolv values for duplicated keys. -// -// Deprecated: will be deleted in a next version. -func OfLoopResolv[S any, K comparable, E, V any](source S, hasNext func(S) bool, getNext func(S) (K, E, error), resolv func(bool, K, V, E) V) (map[K]V, error) { - r := map[K]V{} - for hasNext(source) { - k, elem, err := getNext(source) - if err != nil { - return r, err - } - existVal, ok := r[k] - r[k] = resolv(ok, k, existVal, elem) - } - return r, nil -} - -// GroupOfLoop builds a map of slices by iterating over elements, extracting key\value pairs and grouping the values for each key in the slices. -// The hasNext specifies a predicate that tests existing of a next pair in the source. -// The getNext extracts the pair. -// -// Deprecated: will be deleted in a next version. -func GroupOfLoop[S any, K comparable, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, error)) (map[K][]V, error) { - return OfLoopResolv(source, hasNext, getNext, func(_ bool, _ K, elements []V, val V) []V { - return append(elements, val) - }) -} - // Generate builds a map by an generator function. // The next returns a key\value pair, or false if the generation is over, or an error. func Generate[K comparable, V any](next func() (K, V, bool, error)) (map[K]V, error) { @@ -280,18 +238,6 @@ func ValuesConverted[M ~map[K]V, K comparable, V, Vto any](elements M, by func(V return values } -// Track applies the 'consumer' function for all key/value pairs until the consumer returns the c.Break to stop. -func Track[M ~map[K]V, K comparable, V any](elements M, consumer func(K, V) error) error { - for key, val := range elements { - if err := consumer(key, val); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // TrackEach applies the 'consumer' function for every key/value pairs from the 'elements' map func TrackEach[M ~map[K]V, K comparable, V any](elements M, consumer func(K, V)) { for key, val := range elements { @@ -308,18 +254,6 @@ func TrackWhile[M ~map[K]V, K comparable, V any](elements M, consumer func(K, V) } } -// TrackOrdered applies the 'consumer' function for key/value pairs from the 'elements' map in order of the 'order' slice until the consumer returns the c.Break to stop. -func TrackOrdered[M ~map[K]V, K comparable, V any](order []K, elements M, consumer func(K, V) error) error { - for _, key := range order { - if err := consumer(key, elements[key]); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // TrackEachOrdered applies the 'consumer' function for evey key/value pair from the 'elements' map in order of the 'order' slice func TrackEachOrdered[M ~map[K]V, K comparable, V any](order []K, uniques M, consumer func(K, V)) { for _, key := range order { @@ -363,18 +297,6 @@ func TrackValuesWhile[M ~map[K]V, K comparable, V any](elements M, consumer func } } -// ForKeys applies the 'consumer' function for keys from the 'elements' map until the consumer returns the c.Break to stop. -func ForKeys[M ~map[K]V, K comparable, V any](elements M, consumer func(K) error) error { - for key := range elements { - if err := consumer(key); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // ForEachKey applies the 'consumer' function for every key from from the 'elements' map func ForEachKey[M ~map[K]V, K comparable, V any](elements M, consumer func(K)) { for key := range elements { @@ -382,18 +304,6 @@ func ForEachKey[M ~map[K]V, K comparable, V any](elements M, consumer func(K)) { } } -// ForValues applies the 'consumer' function for values from the 'elements' map until the consumer returns the c.Break to stop.. -func ForValues[M ~map[K]V, K comparable, V any](elements M, consumer func(V) error) error { - for _, val := range elements { - if err := consumer(val); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // ForEachValue applies the 'consumer' function for every value from from the 'elements' map func ForEachValue[M ~map[K]V, K comparable, V any](elements M, consumer func(V)) { for _, val := range elements { @@ -401,19 +311,6 @@ func ForEachValue[M ~map[K]V, K comparable, V any](elements M, consumer func(V)) } } -// ForOrderedValues applies the 'consumer' function for values from the 'elements' map in order of the 'order' slice until the consumer returns the c.Break to stop.. -func ForOrderedValues[M ~map[K]V, K comparable, V any](order []K, elements M, consumer func(V) error) error { - for _, key := range order { - val := elements[key] - if err := consumer(val); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // ForEachOrderedValues applies the 'consumer' function for each value from the 'elements' map in order of the 'order' slice func ForEachOrderedValues[M ~map[K]V, K comparable, V any](order []K, elements M, consumer func(V)) { for _, key := range order { @@ -477,14 +374,20 @@ func Reduce[M ~map[K]V, K comparable, V any](elements M, merge func(K, K, V, V) return rk, rv } -// HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful -func HasAny[M ~map[K]V, K comparable, V any](elements M, predicate func(K, V) bool) bool { +// HasAny checks whether the elements contains an key\value pair that satisfies the condition. +func HasAny[M ~map[K]V, K comparable, V any](elements M, condition func(K, V) bool) bool { + _, _, ok := First(elements, condition) + return ok +} + +// First returns the first key\value pair that satisfies the condition. +func First[M ~map[K]V, K comparable, V any](elements M, condition func(K, V) bool) (k K, v V, ok bool) { for k, v := range elements { - if predicate(k, v) { - return true + if condition(k, v) { + return k, v, true } } - return false + return k, v, false } // Slice collects key\value elements to a slice by applying the specified converter to evety element diff --git a/map_/group/api.go b/map_/group/api.go deleted file mode 100644 index c27626ee..00000000 --- a/map_/group/api.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package group provides short aliases for functions that are used to group key/values retieved from a source -package group - -import ( - "github.com/m4gshm/gollections/map_" -) - -// OfLoop - group.OfLoop synonym for the map_.GroupOfLoop. -// -// Deprecated: will be deleted in a next version. -func OfLoop[S any, K comparable, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, error)) (map[K][]V, error) { - return map_.GroupOfLoop(source, hasNext, getNext) -} diff --git a/map_/iter.go b/map_/iter.go deleted file mode 100644 index 5415f172..00000000 --- a/map_/iter.go +++ /dev/null @@ -1,205 +0,0 @@ -package map_ - -import ( - "unsafe" - - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/kv/collection" - kvloop "github.com/m4gshm/gollections/kv/loop" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/op" -) - -// NewIter returns the Iter based on map elements -func NewIter[K comparable, V any](elements map[K]V) Iter[K, V] { - hmap := *(*unsafe.Pointer)(unsafe.Pointer(&elements)) - i := any(elements) - maptype := *(*unsafe.Pointer)(unsafe.Pointer(&i)) - var iterator *hiter - if hmap != nil { - iterator = new(hiter) - } - return Iter[K, V]{maptype: maptype, hmap: hmap, size: len(elements), iterator: iterator} -} - -// Iter is the embedded map based Iterator implementation -type Iter[K comparable, V any] struct { - iterator *hiter - maptype unsafe.Pointer - hmap unsafe.Pointer - size int -} - -var _ collection.Iterator[int, any] = (*Iter[int, any])(nil) - -// All is used to iterate through the iterator using `for ... range`. -func (i *Iter[K, V]) All(consumer func(key K, value V) bool) { - kvloop.All(i.Next, consumer) -} - -// Track takes key, value pairs retrieved by the iterator. Can be interrupt by returning Break -func (i *Iter[K, V]) Track(traker func(key K, value V) error) error { - return kvloop.Track(i.Next, traker) -} - -// TrackEach takes all key, value pairs retrieved by the iterator -func (i *Iter[K, V]) TrackEach(traker func(key K, value V)) { - kvloop.TrackEach(i.Next, traker) -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *Iter[K, V]) Next() (key K, value V, ok bool) { - if i == nil { - return key, value, false - } - iterator := i.iterator - if iterator == nil { - return key, value, false - } - if !iterator.initialized() { - mapiterinit(i.maptype, i.hmap, iterator) - } else { - mapiternext(iterator) - } - iterkey := mapiterkey(iterator) - if iterkey == nil { - return key, value, false - } - iterelem := mapiterelem(iterator) - key = *(*K)(iterkey) - value = *(*V)(iterelem) - return key, value, true -} - -// Size returns the size of the map -func (i *Iter[K, V]) Size() int { - if i == nil { - return 0 - } - return i.size -} - -//go:linkname mapiterinit reflect.mapiterinit -func mapiterinit(maptype, hmap unsafe.Pointer, it *hiter) - -func mapiterkey(it *hiter) unsafe.Pointer { - return it.key -} - -func mapiterelem(it *hiter) unsafe.Pointer { - return it.elem -} - -//go:linkname mapiternext reflect.mapiternext -func mapiternext(it *hiter) - -// hiter's structure matches runtime.hiter's structure -type hiter struct { - key unsafe.Pointer - elem unsafe.Pointer - t unsafe.Pointer - h unsafe.Pointer - buckets unsafe.Pointer - bptr unsafe.Pointer - overflow *[]unsafe.Pointer - oldoverflow *[]unsafe.Pointer - startBucket uintptr - offset uint8 - wrapped bool - B uint8 - i uint8 - bucket uintptr - checkBucket uintptr -} - -func (h *hiter) initialized() bool { - return h.t != nil -} - -// NewKeyIter instantiates a map keys iterator -func NewKeyIter[K comparable, V any](uniques map[K]V) KeyIter[K, V] { - return KeyIter[K, V]{Iter: NewIter(uniques)} -} - -// KeyIter is the Iterator implementation that provides iterating over keys of a key/value pairs iterator -type KeyIter[K comparable, V any] struct { - Iter[K, V] -} - -var ( - _ c.Iterator[string] = (*KeyIter[string, any])(nil) - _ c.Iterator[string] = KeyIter[string, any]{} -) - -// All is used to iterate through the iterator using `for ... range`. -func (i KeyIter[K, V]) All(consumer func(element K) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i KeyIter[K, V]) For(consumer func(element K) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach FlatIter all elements retrieved by the iterator -func (i KeyIter[K, V]) ForEach(consumer func(element K)) { - loop.ForEach(i.Next, consumer) -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i KeyIter[K, V]) Next() (K, bool) { - key, _, ok := i.Iter.Next() - return key, ok -} - -// Size returns the iterator capacity -func (i KeyIter[K, V]) Size() int { - return i.Iter.Size() -} - -// NewValIter is the main values iterator constructor -func NewValIter[K comparable, V any](uniques map[K]V) ValIter[K, V] { - return ValIter[K, V]{Iter: NewIter(op.IfElse(uniques != nil, uniques, map[K]V{}))} -} - -// ValIter is a map values iterator -type ValIter[K comparable, V any] struct { - Iter[K, V] -} - -var ( - _ c.Iterator[any] = (*ValIter[int, any])(nil) - _ c.Iterator[any] = ValIter[int, any]{} -) - -// All is used to iterate through the iterator using `for ... range`. -func (i ValIter[K, V]) All(consumer func(element V) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i ValIter[K, V]) For(consumer func(element V) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach FlatIter all elements retrieved by the iterator -func (i ValIter[K, V]) ForEach(consumer func(element V)) { - loop.ForEach(i.Next, consumer) -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i ValIter[K, V]) Next() (V, bool) { - _, val, ok := i.Iter.Next() - return val, ok -} - -// Size returns the size of the map -func (i ValIter[K, V]) Size() int { - return i.Iter.Size() -} diff --git a/map_/iter/test/iter_test.go b/map_/iter/test/iter_test.go deleted file mode 100644 index 8824b1db..00000000 --- a/map_/iter/test/iter_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package test - -import ( - "testing" - - "github.com/m4gshm/gollections/map_" - "github.com/stretchr/testify/assert" -) - -func Test_Key_Zero_Safety(t *testing.T) { - var it map_.KeyIter[int, string] - - _, ok := it.Next() - assert.False(t, ok) - assert.Equal(t, 0, it.Size()) - -} - -func Test_OrderedMapIter_Safety(t *testing.T) { - var it map_.Iter[int, string] - - _, _, ok := it.Next() - assert.False(t, ok) - assert.Equal(t, 0, it.Size()) -} diff --git a/map_/test/api_test.go b/map_/test/api_test.go index e8c6b897..0d979224 100644 --- a/map_/test/api_test.go +++ b/map_/test/api_test.go @@ -10,8 +10,6 @@ import ( "github.com/m4gshm/gollections/map_" "github.com/m4gshm/gollections/map_/clone" "github.com/m4gshm/gollections/map_/filter" - "github.com/m4gshm/gollections/map_/group" - "github.com/m4gshm/gollections/map_/resolv" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" "github.com/m4gshm/gollections/slice/clone/sort" @@ -95,36 +93,6 @@ func Test_ValuesConverted(t *testing.T) { assert.Equal(t, slice.Of("1_first", "2_second", "3_third"), sort.Asc(values)) } -type rows[T any] struct { - in []T - cursor int -} - -func (r *rows[T]) hasNext() bool { return r.cursor < len(r.in) } -func (r *rows[T]) next() (T, error) { e := r.in[r.cursor]; r.cursor++; return e, nil } - -func Test_OfLoop(t *testing.T) { - stream := &rows[int]{slice.Of(1, 2, 3), 0} - result, _ := map_.OfLoop(stream, (*rows[int]).hasNext, func(r *rows[int]) (bool, int, error) { - n, err := r.next() - return n%2 == 0, n, err - }) - - assert.Equal(t, 2, result[true]) - assert.Equal(t, 1, result[false]) -} - -func Test_OfLoopResolv(t *testing.T) { - stream := &rows[int]{slice.Of(1, 2, 3, 4), 0} - result, _ := map_.OfLoopResolv(stream, (*rows[int]).hasNext, func(r *rows[int]) (bool, int, error) { - n, err := r.next() - return n%2 == 0, n, err - }, resolv.Last[bool, int]) - - assert.Equal(t, 4, result[true]) - assert.Equal(t, 3, result[false]) -} - func Test_Generate(t *testing.T) { counter := 0 result, _ := map_.Generate(func() (bool, int, bool, error) { counter++; return counter%2 == 0, counter, counter < 4, nil }) @@ -146,17 +114,6 @@ func Test_GenerateResolv(t *testing.T) { assert.Equal(t, 1, result[false]) } -func Test_GroupOfLoop(t *testing.T) { - stream := &rows[int]{slice.Of(1, 2, 3), 0} - result, _ := group.OfLoop(stream, (*rows[int]).hasNext, func(r *rows[int]) (bool, int, error) { - n, err := r.next() - return n%2 == 0, n, err - }) - - assert.Equal(t, slice.Of(2), result[true]) - assert.Equal(t, slice.Of(1, 3), result[false]) -} - func Test_StringRepresentation(t *testing.T) { order := slice.Of(4, 3, 2, 1) elements := map[int]string{4: "4", 2: "2", 1: "1", 3: "3"} diff --git a/op/api.go b/op/api.go index 216dd3b0..a1b7fa13 100644 --- a/op/api.go +++ b/op/api.go @@ -2,32 +2,43 @@ package op import ( - "golang.org/x/exp/constraints" + "cmp" + "fmt" - "github.com/m4gshm/gollections/c" + "golang.org/x/exp/constraints" ) +// Summable is a type that supports the operator + +type Summable interface { + cmp.Ordered | constraints.Complex | string +} + +// Number is a type that supports the operators +, -, /, * +type Number interface { + constraints.Integer | constraints.Float | constraints.Complex +} + // Sum returns the sum of two operands -func Sum[T c.Summable](a T, b T) T { +func Sum[T Summable](a T, b T) T { return a + b } // Sub returns the substraction of the b from the a -func Sub[T c.Number](a T, b T) T { +func Sub[T Number](a T, b T) T { return a - b } // Max returns the maximum from two operands -func Max[T constraints.Ordered](a T, b T) T { +func Max[T cmp.Ordered](a T, b T) T { return IfElse(a < b, b, a) } // Min returns the minimum from two operands -func Min[T constraints.Ordered](a T, b T) T { +func Min[T cmp.Ordered](a T, b T) T { return IfElse(a > b, b, a) } -// IfElse returns the tru value if ok, otherwise return the fal value +// IfElse returns the tru value if ok, otherwise returns the fal value func IfElse[T any](ok bool, tru, fal T) T { if ok { return tru @@ -35,7 +46,16 @@ func IfElse[T any](ok bool, tru, fal T) T { return fal } -// IfElseErr returns the tru value if ok, otherwise return the specified error +// IfElseErrf returns the tru value if ok, otherwise returns an error creating by fmt.Errorf +func IfElseErrf[T any](ok bool, tru T, format string, a ...any) (T, error) { + if ok { + return tru, nil + } + var fal T + return fal, fmt.Errorf(format, a...) +} + +// IfElseErr returns the tru value if ok, otherwise returns the specified error func IfElseErr[T any](ok bool, tru T, err error) (T, error) { if ok { return tru, nil @@ -44,15 +64,40 @@ func IfElseErr[T any](ok bool, tru T, err error) (T, error) { return fal, err } -// IfGetElse exececutes the tru func if ok, otherwise exec the fal function and returns it result -func IfGetElse[T any](ok bool, tru, fal func() T) T { +// IfElseGetErr returns the tru value if ok, otherwise returns an error returnet by the err function +func IfElseGetErr[T any](ok bool, tru T, err func() error) (T, error) { + if ok { + return tru, nil + } + var fal T + return fal, err() +} + +// IfElseGet returns the tru value if ok, otherwise exec the fal function and returns it result +func IfElseGet[T any](ok bool, tru T, fal func() T) T { + if ok { + return tru + } + return fal() +} + +// IfGetElseGet executes the tru func if ok, otherwise exec the fal function and returns it result +func IfGetElseGet[T any](ok bool, tru, fal func() T) T { if ok { return tru() } return fal() } -// IfGetElseGetErr exececutes the tru func if ok, otherwise exec the fal function and returns its error +// IfElseGetWithErr executes the tru func if ok, otherwise exec tthe fal function and returns it result +func IfElseGetWithErr[T any](ok bool, tru T, fal func() (T, error)) (T, error) { + if ok { + return tru, nil + } + return fal() +} + +// IfGetElseGetErr executes the tru func if ok, otherwise exec the fal function and returns its error func IfGetElseGetErr[T any](ok bool, tru func() T, fal func() error) (T, error) { if ok { return tru(), nil @@ -67,7 +112,7 @@ func Get[T any](getter func() T) T { } // Compare returns -1 if o1 less than o2, 0 if equal and 1 if 01 more tha o2 -func Compare[O constraints.Ordered](o1, o2 O) int { +func Compare[O cmp.Ordered](o1, o2 O) int { if o1 < o2 { return -1 } else if o1 > o2 { diff --git a/op/delay/sum/api.go b/op/delay/sum/api.go index 89ca3749..e872c5a5 100644 --- a/op/delay/sum/api.go +++ b/op/delay/sum/api.go @@ -2,17 +2,17 @@ package sum import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/loop" + "github.com/m4gshm/gollections/op" + "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice/sum" ) // Of returns a sum builder function -func Of[T c.Summable](elements ...T) func() T { +func Of[T op.Summable](elements ...T) func() T { return func() T { return sum.Of(elements) } } // Over returns a sum builder function -func Over[T c.Summable](getters ...func() T) func() T { - return func() T { return loop.Sum(loop.Convert(loop.Of(getters...), func(e func() T) T { return e() })) } +func Over[T op.Summable](getters ...func() T) func() T { + return func() T { return seq.Sum(seq.Convert(seq.Of(getters...), func(e func() T) T { return e() })) } } diff --git a/op/sum/api.go b/op/sum/api.go index 5c4afa2d..32e1abc6 100644 --- a/op/sum/api.go +++ b/op/sum/api.go @@ -2,11 +2,11 @@ package sum import ( - "github.com/m4gshm/gollections/c" + "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice/sum" ) // Of an alias of the slice.Sum -func Of[T c.Summable](elements ...T) T { +func Of[T op.Summable](elements ...T) T { return sum.Of(elements) } diff --git a/op/test/api_test.go b/op/test/api_test.go index 83b972ae..7f07e63e 100644 --- a/op/test/api_test.go +++ b/op/test/api_test.go @@ -30,7 +30,7 @@ func Test_IfElseDelay(t *testing.T) { assert.Equal(t, 6, op.IfElse(false, func() int { return 5 }, func() int { return 6 })()) } -func Test_IfDoElse(t *testing.T) { - assert.Equal(t, 5, op.IfGetElse(true, func() int { return 5 }, func() int { return 6 })) - assert.Equal(t, 6, op.IfGetElse(false, func() int { return 5 }, func() int { return 6 })) +func Test_IfGetElseGet(t *testing.T) { + assert.Equal(t, 5, op.IfGetElseGet(true, func() int { return 5 }, func() int { return 6 })) + assert.Equal(t, 6, op.IfGetElseGet(false, func() int { return 5 }, func() int { return 6 })) } diff --git a/predicate/api.go b/predicate/api.go index 571acb40..d825ed65 100644 --- a/predicate/api.go +++ b/predicate/api.go @@ -45,11 +45,12 @@ func Xor[T any](p1, p2 Predicate[T]) Predicate[T] { // Union applies And to predicates func Union[T any](predicates ...Predicate[T]) Predicate[T] { l := len(predicates) - if l == 0 { + switch l { + case 0: return func(_ T) bool { return false } - } else if l == 1 { + case 1: return predicates[0] - } else if l == 2 { + case 2: return And(predicates[0], predicates[1]) } return func(v T) bool { diff --git a/seq/api.go b/seq/api.go index 14c944ba..b0fa40c5 100644 --- a/seq/api.go +++ b/seq/api.go @@ -2,15 +2,18 @@ package seq import ( - "github.com/m4gshm/gollections/c" + "github.com/m4gshm/gollections/convert" + s2 "github.com/m4gshm/gollections/internal/seq2" "github.com/m4gshm/gollections/op" + "github.com/m4gshm/gollections/op/check/not" "github.com/m4gshm/gollections/predicate/always" - "github.com/m4gshm/gollections/seq2" "golang.org/x/exp/constraints" ) -// Seq is an alias of an iterator-function that allows to iterate over elements of a sequence, such as slice. -type Seq[V any] = func(yield func(V) bool) +// Seq is an iterator-function that allows to iterate over elements of a sequence, such as slice. +type Seq[T any] seq[T] + +type seq[T any] = func(func(T) bool) // SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. // It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. @@ -22,25 +25,40 @@ type Seq[V any] = func(yield func(V) bool) // } // ... // } -type SeqE[T any] = Seq2[T, error] +type SeqE[T any] seqE[T] +type seqE[T any] = seq2[T, error] -// Seq2 is an alias of an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. +// Seq2 is an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. // It is used to iterate over slice index/value pairs or map key/value pairs. -type Seq2[K, V any] = func(yield func(K, V) bool) +type Seq2[K, V any] seq2[K, V] + +type seq2[K, V any] = func(func(K, V) bool) // Of creates an iterator over the elements. func Of[T any](elements ...T) Seq[T] { - return func(yield func(T) bool) { + v := func(yield func(T) bool) { for _, v := range elements { if !yield(v) { break } } } + return Seq[T](v) +} + +// Of2 creates an index/value pairs iterator over the elements. +func Of2[T any](elements ...T) Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, v := range elements { + if !yield(i, v) { + break + } + } + } } // Union combines several sequences into one. -func Union[S ~Seq[T], T any](seq ...S) Seq[T] { +func Union[S ~seq[T], T any](seq ...S) Seq[T] { return func(yield func(T) bool) { for _, s := range seq { if s != nil { @@ -167,7 +185,7 @@ func Range[T constraints.Integer | rune](from T, toExclusive T) Seq[T] { } // ToSeq2 converts an iterator of single elements to an iterator of key/value pairs by applying the 'converter' function to each iterable element. -func ToSeq2[S ~Seq[T], T, K, V any](seq S, converter func(T) (K, V)) Seq2[K, V] { +func ToSeq2[S ~seq[T], T, K, V any](seq S, converter func(T) (K, V)) Seq2[K, V] { return func(yield func(K, V) bool) { if seq == nil || converter == nil { return @@ -179,7 +197,7 @@ func ToSeq2[S ~Seq[T], T, K, V any](seq S, converter func(T) (K, V)) Seq2[K, V] } // Top returns a sequence of top n elements. -func Top[S ~Seq[T], T any](n int, seq S) Seq[T] { +func Top[S ~seq[T], T any](n int, seq S) Seq[T] { return func(yield func(T) bool) { if seq == nil { return @@ -195,8 +213,8 @@ func Top[S ~Seq[T], T any](n int, seq S) Seq[T] { } } -// Skip returns a sequence without first n elements. -func Skip[S ~Seq[T], T any](n int, seq S) Seq[T] { +// Skip returns the seq without first n elements. +func Skip[S ~seq[T], T any](n int, seq S) Seq[T] { return func(yield func(T) bool) { if seq == nil { return @@ -212,14 +230,14 @@ func Skip[S ~Seq[T], T any](n int, seq S) Seq[T] { } } -// While cuts tail elements of the seq that don't match the predicate. -func While[S ~Seq[T], T any](seq S, predicate func(T) bool) Seq[T] { +// While cuts tail elements of the seq that don't match the filter. +func While[S ~seq[T], T any](seq S, filter func(T) bool) Seq[T] { return func(yield func(T) bool) { if seq == nil { return } seq(func(t T) bool { - if !predicate(t) { + if !filter(t) { return false } return yield(t) @@ -227,15 +245,15 @@ func While[S ~Seq[T], T any](seq S, predicate func(T) bool) Seq[T] { } } -// SkipWhile returns a sequence without first elements of the seq that dont'math the predicate. -func SkipWhile[S ~Seq[T], T any](seq S, predicate func(T) bool) Seq[T] { +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func SkipWhile[S ~seq[T], T any](seq S, filter func(T) bool) Seq[T] { return func(yield func(T) bool) { if seq == nil { return } started := false seq(func(t T) bool { - if !started && predicate(t) { + if !started && filter(t) { return true } started = true @@ -245,17 +263,17 @@ func SkipWhile[S ~Seq[T], T any](seq S, predicate func(T) bool) Seq[T] { } // Head returns the first element. -func Head[S ~Seq[T], T any](seq S) (v T, ok bool) { +func Head[S ~seq[T], T any](seq S) (v T, ok bool) { return First(seq, always.True) } -// First returns the first element that satisfies the condition of the 'predicate' function. -func First[S ~Seq[T], T any](seq S, predicate func(T) bool) (v T, ok bool) { - if seq == nil || predicate == nil { +// First returns the first element that satisfies the condition. +func First[S ~seq[T], T any](seq S, condition func(T) bool) (v T, ok bool) { + if seq == nil || condition == nil { return } seq(func(one T) bool { - if predicate(one) { + if condition(one) { v = one ok = true return false @@ -265,13 +283,13 @@ func First[S ~Seq[T], T any](seq S, predicate func(T) bool) (v T, ok bool) { return } -// Firstt returns the first element that satisfies the condition of the 'predicate' function. -func Firstt[S ~Seq[T], T any](seq S, predicate func(T) (bool, error)) (v T, ok bool, err error) { - if seq == nil || predicate == nil { +// Firstt returns the first element that satisfies the condition. +func Firstt[S ~seq[T], T any](seq S, condition func(T) (bool, error)) (v T, ok bool, err error) { + if seq == nil || condition == nil { return v, false, nil } seq(func(one T) bool { - ok, err = predicate(one) + ok, err = condition(one) if ok { v = one return false @@ -284,15 +302,12 @@ func Firstt[S ~Seq[T], T any](seq S, predicate func(T) (bool, error)) (v T, ok b } // Slice collects the elements of the 'seq' sequence into a new slice. -func Slice[S ~Seq[T], T any](seq S) []T { +func Slice[S ~seq[T], T any](seq S) []T { return SliceCap(seq, 0) } // SliceCap collects the elements of the 'seq' sequence into a new slice with predefined capacity. -func SliceCap[S ~Seq[T], T any](seq S, capacity int) (out []T) { - if seq == nil { - return nil - } +func SliceCap[S ~seq[T], T any](seq S, capacity int) (out []T) { if capacity > 0 { out = make([]T, 0, capacity) } @@ -300,7 +315,7 @@ func SliceCap[S ~Seq[T], T any](seq S, capacity int) (out []T) { } // Append collects the elements of the 'seq' sequence into the specified 'out' slice. -func Append[S ~Seq[T], TS ~[]T, T any](seq S, out TS) TS { +func Append[S ~seq[T], TS ~[]T, T any](seq S, out TS) TS { if seq == nil { return out } @@ -311,15 +326,15 @@ func Append[S ~Seq[T], TS ~[]T, T any](seq S, out TS) TS { return out } -// Reduce reduces the elements of the 'seq' sequence an one using the 'merge' function. -func Reduce[S ~Seq[T], T any](seq S, merge func(T, T) T) T { +// Reduce reduces the elements of the seq into one using the 'merge' function. +func Reduce[S ~seq[T], T any](seq S, merge func(T, T) T) T { result, _ := ReduceOK(seq, merge) return result } -// ReduceOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). -func ReduceOK[S ~Seq[T], T any](seq S, merge func(T, T) T) (result T, ok bool) { +func ReduceOK[S ~seq[T], T any](seq S, merge func(T, T) T) (result T, ok bool) { if seq == nil || merge == nil { return result, false } @@ -336,15 +351,15 @@ func ReduceOK[S ~Seq[T], T any](seq S, merge func(T, T) T) (result T, ok bool) { return result, started } -// Reducee reduces the elements of the 'seq' sequence an one using the 'merge' function. -func Reducee[S ~Seq[T], T any](seq S, merge func(T, T) (T, error)) (T, error) { +// Reducee reduces the elements of the seq into one using the 'merge' function. +func Reducee[S ~seq[T], T any](seq S, merge func(T, T) (T, error)) (T, error) { result, _, err := ReduceeOK(seq, merge) return result, err } -// ReduceeOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). -func ReduceeOK[S ~Seq[T], T any](seq S, merge func(T, T) (T, error)) (result T, ok bool, err error) { +func ReduceeOK[S ~seq[T], T any](seq S, merge func(T, T) (T, error)) (result T, ok bool, err error) { if seq == nil || merge == nil { return result, false, nil } @@ -354,17 +369,15 @@ func ReduceeOK[S ~Seq[T], T any](seq S, merge func(T, T) (T, error)) (result T, result = v started = true return true - } else { - result, err = merge(result, v) - return err == nil } - + result, err = merge(result, v) + return err == nil }) return result, started, err } // 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 of the 'seq' sequence. -func Accum[T any, S ~Seq[T]](first T, seq S, merge func(T, T) T) T { +func Accum[T any, S ~seq[T]](first T, seq S, merge func(T, T) T) T { accumulator := first if seq == nil || merge == nil { return accumulator @@ -378,7 +391,7 @@ func Accum[T any, S ~Seq[T]](first T, seq S, merge func(T, T) T) T { } // 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 of the 'seq' sequence. -func Accumm[T any, S ~Seq[T]](first T, seq S, merge func(T, T) (T, error)) (accumulator T, err error) { +func Accumm[T any, S ~seq[T]](first T, seq S, merge func(T, T) (T, error)) (accumulator T, err error) { accumulator = first if seq == nil || merge == nil { return accumulator, nil @@ -392,18 +405,18 @@ func Accumm[T any, S ~Seq[T]](first T, seq S, merge func(T, T) (T, error)) (accu } // Sum returns the sum of all elements. -func Sum[S ~Seq[T], T c.Summable](seq S) (out T) { +func Sum[S ~seq[T], T op.Summable](seq S) (out T) { return Accum(out, seq, op.Sum[T]) } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful. -func HasAny[S ~Seq[T], T any](seq S, predicate func(T) bool) bool { - _, ok := First(seq, predicate) +// HasAny checks whether the seq contains an element that satisfies the condition. +func HasAny[S ~seq[T], T any](seq S, filter func(T) bool) bool { + _, ok := First(seq, filter) return ok } // Contains finds the first element that equal to the example and returns true. -func Contains[S ~Seq[T], T comparable](seq S, example T) bool { +func Contains[S ~seq[T], T comparable](seq S, example T) bool { if seq == nil { return false } @@ -415,7 +428,7 @@ func Contains[S ~Seq[T], T comparable](seq S, example T) bool { return contains } -// Conv creates an iterator that applies the 'converter' function to each iterable element and returns value-error pairs. +// Conv creates an errorable seq that applies the 'converter' function to the iterable elements. // The error should be checked at every iteration step, like: // // var integers iter.Seq[int] @@ -426,12 +439,12 @@ func Contains[S ~Seq[T], T comparable](seq S, example T) bool { // } // ... // } -func Conv[S ~Seq[From], From, To any](seq S, converter func(From) (To, error)) SeqE[To] { +func Conv[S ~seq[From], From, To any](seq S, converter func(From) (To, error)) SeqE[To] { return SeqE[To](ToSeq2(seq, converter)) } // Convert creates an iterator that applies the 'converter' function to each iterable element. -func Convert[S ~Seq[From], From, To any](seq S, converter func(From) To) Seq[To] { +func Convert[S ~seq[From], From, To any](seq S, converter func(From) To) Seq[To] { return func(yield func(To) bool) { if seq == nil || converter == nil { return @@ -442,9 +455,14 @@ func Convert[S ~Seq[From], From, To any](seq S, converter func(From) To) Seq[To] } } +// ConvertNilSafe creates a seq that filters not nil elements, converts that ones, filters not nils after converting and returns them. +func ConvertNilSafe[S ~seq[*From], From, To any](seq S, converter func(*From) *To) Seq[*To] { + return ConvertOK(seq, convert.NilSafe(converter)) +} + // ConvertOK creates an iterator that applies the 'converter' function to each iterable element. -// The converter may returns a value or ok=false to exclude the value from the loop. -func ConvertOK[S ~Seq[From], From, To any](seq S, converter func(from From) (To, bool)) Seq[To] { +// The converter may returns a value or ok=false to exclude the value from the sequence. +func ConvertOK[S ~seq[From], From, To any](seq S, converter func(from From) (To, bool)) Seq[To] { return func(yield func(To) bool) { if seq == nil || converter == nil { return @@ -461,7 +479,7 @@ func ConvertOK[S ~Seq[From], From, To any](seq S, converter func(from From) (To, // ConvOK creates a iterator that applies the 'converter' function to each iterable element. // The converter may returns a value or ok=false to exclude the value from iteration. // It may also return an error to abort the iteration. -func ConvOK[S ~Seq[From], From, To any](seq S, converter func(from From) (To, bool, error)) SeqE[To] { +func ConvOK[S ~seq[From], From, To any](seq S, converter func(from From) (To, bool, error)) SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || converter == nil { return @@ -482,7 +500,7 @@ func ConvOK[S ~Seq[From], From, To any](seq S, converter func(from From) (To, bo // for e := range seq.Flat(arrays, as.Is) { // ... // } -func Flat[S ~Seq[From], STo ~[]To, From any, To any](seq S, flattener func(From) STo) Seq[To] { +func Flat[S ~seq[From], STo ~[]To, From any, To any](seq S, flattener func(From) STo) Seq[To] { return func(yield func(To) bool) { if seq == nil || flattener == nil { return @@ -503,10 +521,10 @@ func Flat[S ~Seq[From], STo ~[]To, From any, To any](seq S, flattener func(From) // // var arrays iter.Seq[[]int] // ... -// for e := range seq.FlatSeq(arrays, slices.Values) { +// for e := range s.FlatSeq(arrays, slices.Values) { // ... // } -func FlatSeq[S ~Seq[From], STo ~Seq[To], From any, To any](seq S, flattener func(From) STo) Seq[To] { +func FlatSeq[S ~seq[From], STo ~seq[To], From any, To any](seq S, flattener func(From) STo) Seq[To] { return func(yield func(To) bool) { if seq == nil || flattener == nil { return @@ -541,7 +559,7 @@ func FlatSeq[S ~Seq[From], STo ~Seq[To], From any, To any](seq S, flattener func // } // ... // } -func Flatt[S ~Seq[From], STo ~[]To, From any, To any](seq S, flattener func(From) (STo, error)) SeqE[To] { +func Flatt[S ~seq[From], STo ~[]To, From any, To any](seq S, flattener func(From) (STo, error)) SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -579,7 +597,7 @@ func Flatt[S ~Seq[From], STo ~[]To, From any, To any](seq S, flattener func(From // } // ... // } -func FlattSeq[S ~Seq[From], STo ~SeqE[To], From any, To any](seq S, flattener func(From) STo) SeqE[To] { +func FlattSeq[S ~seq[From], STo ~seqE[To], From any, To any](seq S, flattener func(From) STo) SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -598,7 +616,7 @@ func FlattSeq[S ~Seq[From], STo ~SeqE[To], From any, To any](seq S, flattener fu } // Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. -func Filter[S ~Seq[T], T any](seq S, filter func(T) bool) Seq[T] { +func Filter[S ~seq[T], T any](seq S, filter func(T) bool) Seq[T] { return func(yield func(T) bool) { if seq == nil || filter == nil { return @@ -613,7 +631,7 @@ func Filter[S ~Seq[T], T any](seq S, filter func(T) bool) Seq[T] { } // Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. -func Filt[S ~Seq[T], T any](seq S, filter func(T) (bool, error)) SeqE[T] { +func Filt[S ~seq[T], T any](seq S, filter func(T) (bool, error)) SeqE[T] { return func(yield func(T, error) bool) { if seq == nil || filter == nil { return @@ -627,13 +645,13 @@ func Filt[S ~Seq[T], T any](seq S, filter func(T) (bool, error)) SeqE[T] { } } -// KeyValue converts the seq iterator to a key/value pairs iterator by applying the key, value extractors to each iterable element. -func KeyValue[S ~Seq[T], T, K, V any](seq S, keyExtractor func(T) K, valExtractor func(T) V) Seq2[K, V] { +// ToKV converts the seq iterator to a key/value pairs iterator by applying the key, value extractors to each iterable element. +func ToKV[S ~seq[T], T, K, V any](seq S, keyExtractor func(T) K, valExtractor func(T) V) Seq2[K, V] { return ToSeq2(seq, func(t T) (K, V) { return keyExtractor(t), valExtractor(t) }) } // KeyValues converts the seq iterator to a key/value pairs iterator by applying the key, values extractors to each iterable element. -func KeyValues[S ~Seq[T], T, K, V any](seq S, keyExtractor func(T) K, valsExtractor func(T) []V) Seq2[K, V] { +func KeyValues[S ~seq[T], T, K, V any](seq S, keyExtractor func(T) K, valsExtractor func(T) []V) Seq2[K, V] { return func(yield func(K, V) bool) { if seq == nil || keyExtractor == nil || valsExtractor == nil { return @@ -653,11 +671,16 @@ func KeyValues[S ~Seq[T], T, K, V any](seq S, keyExtractor func(T) K, valsExtrac // Group collects the seq elements into a new map. // The keyExtractor converts an element to a key. // The valExtractor converts an element to a value. -func Group[S ~Seq[T], T any, K comparable, V any](seq S, keyExtractor func(T) K, valExtractor func(T) V) map[K][]V { - return seq2.Group(KeyValue(seq, keyExtractor, valExtractor)) +func Group[S ~seq[T], T any, K comparable, V any](seq S, keyExtractor func(T) K, valExtractor func(T) V) map[K][]V { + return s2.Group(ToKV(seq, keyExtractor, valExtractor)) +} + +// NotNil returns teh seq without nil elements. +func NotNil[T any](seq Seq[*T]) Seq[*T] { + return Filter(seq, not.Nil[T]) } -// ForEach applies the 'consumer' function to the seq elements +// ForEach applies the 'consumer' function to the seq elements. func ForEach[T any](seq Seq[T], consumer func(T)) { if seq == nil { return diff --git a/seq/seq2_api.go b/seq/seq2_api.go new file mode 100644 index 00000000..a1d10155 --- /dev/null +++ b/seq/seq2_api.go @@ -0,0 +1,96 @@ +package seq + +import ( + "github.com/m4gshm/gollections/c" + s2 "github.com/m4gshm/gollections/internal/seq2" +) + +// Head returns the first key\value pair. +func (s Seq2[K, V]) Head() (K, V, bool) { + return s2.Head(s) +} + +// First returns the first key\value pair that satisfies the condition. +func (s Seq2[K, V]) First(condition func(K, V) bool) (K, V, bool) { + return s2.First(s, condition) +} + +// Firstt returns the first key\value pair that satisfies the condition. +func (s Seq2[K, V]) Firstt(condition func(K, V) (bool, error)) (K, V, bool, error) { + return s2.Firstt(s, condition) +} + +// HasAny checks whether the seq contains a key\value pair that satisfies the condition. +func (s Seq2[K, V]) HasAny(condition func(K, V) bool) bool { + return s2.HasAny(s, condition) +} + +// Union combines several sequences into one. +func (s Seq2[K, V]) Union(seqences ...seq2[K, V]) Seq2[K, V] { + return s2.Union(append(append(make([]seq2[K, V], len(seqences)+1), s), seqences...)...) +} + +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func (s Seq2[K, V]) Filter(filter func(K, V) bool) Seq2[K, V] { + return s2.Filter(s, filter) +} + +// Filt creates an erroreable iterator that iterates only those key\value pairs for which the 'filter' function returns true. +func (s Seq2[K, V]) Filt(filter func(K, V) (bool, error)) SeqE[c.KV[K, V]] { + return s2.Filt(s, filter) +} + +// Convert creates an iterator that applies the 'converter' function to each iterable key\value pair. +func (s Seq2[K, V]) Convert(converter func(K, V) (K, V)) Seq2[K, V] { + return s2.Convert(s, converter) +} + +// Conv creates an errorable seq that applies the 'converter' function to the iterable key\value pairs. +func (s Seq2[K, V]) Conv(converter func(K, V) (K, V, error)) SeqE[c.KV[K, V]] { + return s2.Conv(s, converter) +} + +// Keys converts a key/value pairs iterator to an iterator of just keys. +func (s Seq2[K, V]) Keys() Seq[K] { + return s2.Keys(s) +} + +// Values converts a key/value pairs iterator to an iterator of just values. +func (s Seq2[K, V]) Values() Seq[V] { + return s2.Values(s) +} + +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function. +func (s Seq2[K, V]) FilterKey(filter func(K) bool) Seq2[K, V] { + return s2.FilterKey(s, filter) +} + +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function. +func (s Seq2[K, V]) FilterValue(filter func(V) bool) Seq2[K, V] { + return s2.FilterValue(s, filter) +} + +// ConvertKey returns a seq that applies the 'converter' function to keys. +func (s Seq2[K, V]) ConvertKey(converter func(K) K) Seq2[K, V] { + return s2.ConvertKey(s, converter) +} + +// ConvKey returns a seq that applies the 'converter' function to keys. +func (s Seq2[K, V]) ConvKey(converter func(K) (K, error)) SeqE[c.KV[K, V]] { + return s2.ConvKey(s, converter) +} + +// ConvertValue returns a seq that applies the 'converter' function to values. +func (s Seq2[K, V]) ConvertValue(converter func(V) V) Seq2[K, V] { + return s2.ConvertValue(s, converter) +} + +// ConvValue returns a seq that applies the 'converter' function to values. +func (s Seq2[K, V]) ConvValue(converter func(V) (V, error)) SeqE[c.KV[K, V]] { + return s2.ConvValue(s, converter) +} + +// TrackEach applies the 'consumer' function to the seq key\value pairs. +func (s Seq2[K, V]) TrackEach(consumer func(K, V)) { + s2.TrackEach(s, consumer) +} diff --git a/seq/seq_api.go b/seq/seq_api.go new file mode 100644 index 00000000..5d4a7bc9 --- /dev/null +++ b/seq/seq_api.go @@ -0,0 +1,113 @@ +package seq + +// Slice collects the elements of the 'seq' sequence into a new slice. +func (s Seq[T]) Slice() []T { + return Slice(s) +} + +// Append collects the elements of the 'seq' sequence into the specified 'out' slice. +func (s Seq[T]) Append(out []T) []T { + return Append(s, out) +} + +// Reduce reduces the elements of the seq into one using the 'merge' function. +func (s Seq[T]) Reduce(merge func(a T, b T) T) T { + return Reduce(s, merge) +} + +// ReduceOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func (s Seq[T]) ReduceOK(merge func(T, T) T) (result T, ok bool) { + return ReduceOK(s, merge) +} + +// Reducee reduces the elements of the seq into one using the 'merge' function. +func (s Seq[T]) Reducee(merge func(T, T) (T, error)) (T, error) { + return Reducee(s, merge) +} + +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func (s Seq[T]) ReduceeOK(merge func(T, T) (T, error)) (result T, ok bool, err error) { + return ReduceeOK(s, merge) +} + +// 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 of the 'seq' sequence. +func (s Seq[T]) Accum(first T, merge func(T, T) T) T { + return Accum(first, s, merge) +} + +// 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 of the 'seq' sequence. +func (s Seq[T]) Accumm(first T, merge func(T, T) (T, error)) (T, error) { + return Accumm(first, s, merge) +} + +// Head returns the first element. +func (s Seq[T]) Head() (v T, ok bool) { + return Head(s) +} + +// First returns the first element that satisfies the condition. +func (s Seq[T]) First(predicate func(T) bool) (v T, ok bool) { + return First(s, predicate) +} + +// Firstt returns the first element that satisfies the condition. +func (s Seq[T]) Firstt(predicate func(T) (bool, error)) (v T, ok bool, err error) { + return Firstt(s, predicate) +} + +// Top returns a sequence of top n elements. +func (s Seq[T]) Top(n int) Seq[T] { + return Top(n, s) +} + +// Skip returns the seq without first n elements. +func (s Seq[T]) Skip(n int) Seq[T] { + return Skip(n, s) +} + +// While cuts tail elements of the seq that don't match the filter. +func (s Seq[T]) While(filter func(T) bool) Seq[T] { + return While(s, filter) +} + +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func (s Seq[T]) SkipWhile(filter func(T) bool) Seq[T] { + return SkipWhile(s, filter) +} + +// HasAny checks whether the seq contains an element that satisfies the condition. +func (s Seq[T]) HasAny(predicate func(T) bool) bool { + return HasAny(s, predicate) +} + +// Union combines several sequences into one. +func (s Seq[T]) Union(seqences ...seq[T]) Seq[T] { + return Union(append(append(make([]seq[T], len(seqences)+1), s), seqences...)...) +} + +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func (s Seq[T]) Filter(filter func(s T) bool) Seq[T] { + return Filter(s, filter) +} + +// Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. +func (s Seq[T]) Filt(filter func(s T) (bool, error)) SeqE[T] { + return Filt(s, filter) +} + +// Convert creates an iterator that applies the 'converter' function to each iterable element. +func (s Seq[T]) Convert(converter func(t T) T) Seq[T] { + return Convert(s, converter) +} + +// Conv creates an errorable seq that applies the 'converter' function to the iterable elements. +func (s Seq[T]) Conv(converter func(T) (T, error)) SeqE[T] { + return Conv(s, converter) +} + +// ForEach applies the 'consumer' function to the seq elements. +func (s Seq[T]) ForEach(f func(T)) { + ForEach(s, f) +} diff --git a/seq/seqe_api.go b/seq/seqe_api.go new file mode 100644 index 00000000..5fce01a5 --- /dev/null +++ b/seq/seqe_api.go @@ -0,0 +1,115 @@ +package seq + +import "github.com/m4gshm/gollections/internal/seqe" + +// Slice collects the elements of the 'seq' sequence into a new slice. +func (s SeqE[T]) Slice() ([]T, error) { + return seqe.Slice(s) +} + +// Append collects the elements of the 'seq' sequence into the specified 'out' slice. +func (s SeqE[T]) Append(out []T) ([]T, error) { + return seqe.Append(s, out) +} + +// Reduce reduces the elements of the seq into one using the 'merge' function. +func (s SeqE[T]) Reduce(merge func(a T, b T) T) (T, error) { + return seqe.Reduce(s, merge) +} + +// ReduceOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func (s SeqE[T]) ReduceOK(merge func(T, T) T) (result T, ok bool, err error) { + return seqe.ReduceOK(s, merge) +} + +// Reducee reduces the elements of the seq into one using the 'merge' function. +func (s SeqE[T]) Reducee(merge func(T, T) (T, error)) (T, error) { + return seqe.Reducee(s, merge) +} + +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. +// Returns ok==false if the seq returns ok=false at the first call (no more elements). +func (s SeqE[T]) ReduceeOK(merge func(T, T) (T, error)) (result T, ok bool, err error) { + return seqe.ReduceeOK(s, merge) +} + +// 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 of the 'seq' sequence. +func (s SeqE[T]) Accum(first T, merge func(T, T) T) (T, error) { + return seqe.Accum(first, s, merge) +} + +// 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 of the 'seq' sequence. +func (s SeqE[T]) Accumm(first T, merge func(T, T) (T, error)) (T, error) { + return seqe.Accumm(first, s, merge) +} + +// Head returns the first element. +func (s SeqE[T]) Head() (T, bool, error) { + return seqe.Head(s) +} + +// First returns the first element that satisfies the condition. +func (s SeqE[T]) First(predicate func(T) bool) (T, bool, error) { + return seqe.First(s, predicate) +} + +// Firstt returns the first element that satisfies the condition. +func (s SeqE[T]) Firstt(predicate func(T) (bool, error)) (T, bool, error) { + return seqe.Firstt(s, predicate) +} + +// Top returns a sequence of top n elements. +func (s SeqE[T]) Top(n int) SeqE[T] { + return seqe.Top(n, s) +} + +// Skip returns the seq without first n elements. +func (s SeqE[T]) Skip(n int) SeqE[T] { + return seqe.Skip(n, s) +} + +// While cuts tail elements of the seq that don't match the filter. +func (s SeqE[T]) While(filter func(T) bool) SeqE[T] { + return seqe.While(s, filter) +} + +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func (s SeqE[T]) SkipWhile(filter func(T) bool) SeqE[T] { + return seqe.SkipWhile(s, filter) +} + +// HasAny checks whether the seq contains an element that satisfies the condition. +func (s SeqE[T]) HasAny(predicate func(T) bool) (bool, error) { + return seqe.HasAny(s, predicate) +} + +// Union combines several sequences into one. +func (s SeqE[T]) Union(seqences ...seqE[T]) SeqE[T] { + return seqe.Union(append(append(make([]seqE[T], len(seqences)+1), s), seqences...)...) +} + +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func (s SeqE[T]) Filter(filter func(s T) bool) SeqE[T] { + return seqe.Filter(s, filter) +} + +// Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. +func (s SeqE[T]) Filt(filter func(s T) (bool, error)) SeqE[T] { + return seqe.Filt(s, filter) +} + +// Convert creates an iterator that applies the 'converter' function to each iterable element. +func (s SeqE[T]) Convert(converter func(t T) T) SeqE[T] { + return seqe.Convert(s, converter) +} + +// Conv creates an errorable seq that applies the 'converter' function to the collection elements. +func (s SeqE[T]) Conv(converter func(T) (T, error)) SeqE[T] { + return seqe.Conv(s, converter) +} + +// ForEach applies the 'consumer' function to the seq elements. +func (s SeqE[T]) ForEach(f func(T)) error { + return seqe.ForEach(s, f) +} diff --git a/seq/test/api_benchmark_test.go b/seq/test/api_benchmark_test.go index 86f4a592..e9afa095 100644 --- a/seq/test/api_benchmark_test.go +++ b/seq/test/api_benchmark_test.go @@ -3,7 +3,6 @@ package test import ( "testing" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/slice/range_" ) @@ -36,18 +35,3 @@ func Benchmark_Loop_Seq_Filter_Seq(b *testing.B) { } b.StopTimer() } - -func Benchmark_Loop_Loop_Filter_Seq(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - next := loop.Filter(loop.Of(values...), even) - for { - e, ok := next() - if !ok { - break - } - _ = e - } - } - b.StopTimer() -} diff --git a/seq/test/api_test.go b/seq/test/api_test.go index 38806848..4189b653 100644 --- a/seq/test/api_test.go +++ b/seq/test/api_test.go @@ -57,6 +57,9 @@ func Test_Union(t *testing.T) { r = append(r, i) } assert.Equal(t, slice.Of(0, 1, 2, 3), r) + + sequence = seq.Of(0, 1).Union(nil, seq.Of[int]()).Union(seq.Of(2, 3, 4)) + assert.Equal(t, slice.Of(0, 1, 2, 3, 4), seq.Slice(sequence)) } func Test_OfIndexed(t *testing.T) { @@ -112,8 +115,11 @@ func Test_Append(t *testing.T) { out := seq.Append[seq.Seq[int]](nil, in) assert.Equal(t, in, out) - out = seq.Append(seq.Of(2), in) + out = seq.Append(seq.Of(2), out) assert.Equal(t, []int{1, 2}, out) + + out = seq.Of(3).Append(out) + assert.Equal(t, []int{1, 2, 3}, out) } func Test_AccumSum(t *testing.T) { @@ -125,16 +131,24 @@ func Test_AccumSum(t *testing.T) { r = seq.Sum(s) assert.Equal(t, 1+3+5+7+9+11, r) + + r = s.Accum(100, op.Sum[int]) + assert.Equal(t, 100+1+3+5+7+9+11, r) } func Test_AccummSum(t *testing.T) { s := seq.Of(1, 3, 5, 7, 9, 11) - r, err := seq.Accumm(100, s, func(i1, i2 int) (int, error) { + summator := func(i1, i2 int) (int, error) { if i2 == 11 { return i1, errors.New("stop") } return i1 + i2, nil - }) + } + r, err := seq.Accumm(100, s, summator) + assert.Equal(t, 100+1+3+5+7+9, r) + assert.ErrorContains(t, err, "stop") + + r, err = s.Accumm(100, summator) assert.Equal(t, 100+1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") } @@ -144,6 +158,11 @@ func Test_ReduceSum(t *testing.T) { assert.True(t, ok) assert.Equal(t, 21, sum) + + sum, ok = seq.Of(1, 2, 3, 4, 5, 6).ReduceOK(op.Sum) + + assert.True(t, ok) + assert.Equal(t, 21, sum) } func Test_ReduceeSum(t *testing.T) { @@ -159,6 +178,11 @@ func Test_ReduceeSum(t *testing.T) { assert.Equal(t, 1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") + r, ok, err = s.ReduceeOK(reducer) + assert.True(t, ok) + assert.Equal(t, 1+3+5+7+9, r) + assert.ErrorContains(t, err, "stop") + _, ok, err = seq.ReduceeOK[seq.Seq[int]](nil, reducer) assert.False(t, ok) assert.NoError(t, err) @@ -166,6 +190,10 @@ func Test_ReduceeSum(t *testing.T) { r, err = seq.Reducee(s, reducer) assert.Equal(t, 1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") + + r, err = s.Reducee(reducer) + assert.Equal(t, 1+3+5+7+9, r) + assert.ErrorContains(t, err, "stop") } func Test_ReduceeSumFirstErr(t *testing.T) { @@ -204,6 +232,11 @@ func Test_Head(t *testing.T) { result, ok = seq.Head[seq.Seq[int]](nil) assert.Zero(t, result) assert.False(t, ok) + + result, ok = sequence.Head() + + assert.True(t, ok) + assert.Equal(t, 1, result) } func Test_While(t *testing.T) { @@ -212,6 +245,10 @@ func Test_While(t *testing.T) { assert.Equal(t, slice.Of(1, 2, 3, 4), seq.Slice(part)) + part = sequence.While(not.Eq(5)) + + assert.Equal(t, slice.Of(1, 2, 3, 4), seq.Slice(part)) + part = seq.While(sequence, not.Eq(7)) assert.Equal(t, slice.Of(1, 2, 3, 4, 5, 6), seq.Slice(part)) @@ -237,6 +274,10 @@ func Test_SkipWhile(t *testing.T) { assert.Equal(t, slice.Of(4, 5, 6), seq.Slice(part)) + part = sequence.SkipWhile(less.Than(4)) + + assert.Equal(t, slice.Of(4, 5, 6), seq.Slice(part)) + part = seq.SkipWhile(sequence, not.Eq(7)) assert.Nil(t, seq.Slice(part)) @@ -262,6 +303,10 @@ func Test_Top(t *testing.T) { assert.Equal(t, slice.Of(1, 2, 3, 4), result) assert.Equal(t, result2, result) + top = sequence.Top(4) + result = seq.Slice(top) + assert.Equal(t, result2, result) + result = seq.Slice(seq.Top(0, sequence)) assert.Nil(t, result) @@ -277,6 +322,15 @@ func Test_Top(t *testing.T) { } } assert.Equal(t, slice.Of(1, 2), result) + result = nil + for v := range seq.Of(1, 2, 3, 4, 5, 6).Top(4) { + if v != 3 { + result = append(result, v) + } else { + break + } + } + assert.Equal(t, slice.Of(1, 2), result) } func Test_Skip(t *testing.T) { @@ -288,6 +342,9 @@ func Test_Skip(t *testing.T) { assert.Equal(t, slice.Of(5, 6), result) assert.Equal(t, result2, result) + skip = sequence.Skip(4) + assert.Equal(t, result2, result) + result = seq.Slice(seq.Skip(0, sequence)) assert.Equal(t, seq.Slice(sequence), result) @@ -320,7 +377,13 @@ func Test_First(t *testing.T) { assert.True(t, ok) assert.Equal(t, 6, result) + result, ok = sequence.First(more.Than(5)) + + assert.True(t, ok) + assert.Equal(t, 6, result) + assert.True(t, seq.HasAny(sequence, more.Than(5))) + assert.True(t, sequence.HasAny(more.Than(5))) _, ok = seq.First[seq.Seq[int]](nil, more.Than(5)) assert.False(t, ok) @@ -334,21 +397,36 @@ func Test_First(t *testing.T) { func Test_Firstt(t *testing.T) { sequence := seq.Of(1, 2, 3, 4, 5, 6) - result, ok, err := seq.Firstt(sequence, func(i int) (bool, error) { + noErrCond := func(i int) (bool, error) { return more.Than(5)(i), nil - }) + } + result, ok, err := seq.Firstt(sequence, noErrCond) + + assert.True(t, ok) + assert.Equal(t, 6, result) + assert.NoError(t, err) + + result, ok, err = sequence.Firstt(noErrCond) assert.True(t, ok) assert.Equal(t, 6, result) assert.NoError(t, err) - result, ok, err = seq.Firstt(sequence, func(_ int) (bool, error) { return true, errors.New("abort") }) + firstOkButErr := func(_ int) (bool, error) { return true, errors.New("abort") } + result, ok, err = seq.Firstt(sequence, firstOkButErr) + + assert.True(t, ok) + assert.Equal(t, 1, result) + assert.ErrorContains(t, err, "abort") + + result, ok, err = sequence.Firstt(firstOkButErr) assert.True(t, ok) assert.Equal(t, 1, result) assert.ErrorContains(t, err, "abort") - result, ok, err = seq.Firstt(sequence, func(_ int) (bool, error) { return false, errors.New("abort") }) + allErr := func(_ int) (bool, error) { return false, errors.New("abort") } + result, ok, err = seq.Firstt(sequence, allErr) assert.False(t, ok) assert.Equal(t, 0, result) @@ -357,7 +435,7 @@ func Test_Firstt(t *testing.T) { _, ok, _ = seq.Firstt(sequence, nil) assert.False(t, ok) - _, ok, _ = seq.Firstt[seq.Seq[int]](nil, func(_ int) (bool, error) { return false, errors.New("abort") }) + _, ok, _ = seq.Firstt[seq.Seq[int]](nil, allErr) assert.False(t, ok) } @@ -499,6 +577,9 @@ func Test_Filter(t *testing.T) { s := seq.Of(1, 3, 4, 5, 7, 8, 9, 11) r := seq.Filter(s, even) assert.Equal(t, slice.Of(4, 8), seq.Slice(r)) + + r = s.Filter(even) + assert.Equal(t, slice.Of(4, 8), seq.Slice(r)) } func Test_Filt(t *testing.T) { @@ -510,6 +591,11 @@ func Test_Filt(t *testing.T) { assert.Error(t, err) assert.Equal(t, slice.Of(4), r) + r, err = s.Filt(filter).Slice() + + assert.Error(t, err) + assert.Equal(t, slice.Of(4), r) + l = seq.Filt(s, nil) r, err = seqe.Slice(l) @@ -519,11 +605,15 @@ func Test_Filt(t *testing.T) { func Test_Filt2(t *testing.T) { s := seq.Of(1, 3, 4, 5, 7, 8, 9, 11) - l := seq.Filt(s, func(i int) (bool, error) { + cond := func(i int) (bool, error) { ok := i <= 7 return ok && even(i), op.IfElse(ok, nil, errors.New("abort")) - }) - r, err := seqe.Slice(l) + } + r, err := seqe.Slice(seq.Filt(s, cond)) + assert.Error(t, err) + assert.Equal(t, slice.Of(4), r) + + r, err = seq.Filt(s, cond).Slice() assert.Error(t, err) assert.Equal(t, slice.Of(4), r) } @@ -574,7 +664,7 @@ func Test_OfNextPush(t *testing.T) { func Test_KeyValue(t *testing.T) { s := seq.Of(1, 2, 3) - s2 := seq.KeyValue(s, as.Is, strconv.Itoa) + s2 := seq.ToKV(s, as.Is, strconv.Itoa) k := seq.Slice(seq2.Keys(s2)) v := seq.Slice(seq2.Values(s2)) @@ -591,6 +681,12 @@ func Test_KeyValues(t *testing.T) { assert.Equal(t, slice.Of(2, 2, 1), keys) assert.Equal(t, slice.Of(1, 2, 3), vals) + keys = s2.Keys().Slice() + vals = s2.Values().Slice() + + assert.Equal(t, slice.Of(2, 2, 1), keys) + assert.Equal(t, slice.Of(1, 2, 3), vals) + keys = nil vals = nil for k, v := range s2 { @@ -683,6 +779,20 @@ func Test_Conv(t *testing.T) { assert.Equal(t, slice.Of(1, 2, 3, 5, 8, 9, 11), i) } +func Test_ConvertNilSafe(t *testing.T) { + type entity struct{ val *string } + var ( + first = "first" + third = "third" + fifth = "fifth" + source = seq.Of([]*entity{{&first}, {}, {&third}, nil, {&fifth}}...) + result = seq.ConvertNilSafe(source, func(e *entity) *string { return e.val }) + expected = []*string{&first, &third, &fifth} + ) + s := result.Slice() + assert.Equal(t, expected, s) +} + func Test_ConvertOK(t *testing.T) { s := seq.Of(1, 3, 4, 5, 7, 8, 9, 11) converter := func(i int) (string, bool) { return strconv.Itoa(i), even(i) } diff --git a/seq2/api.go b/seq2/api.go index 5d4591cb..428ba524 100644 --- a/seq2/api.go +++ b/seq2/api.go @@ -2,23 +2,29 @@ package seq2 import ( + "golang.org/x/exp/constraints" + + "github.com/m4gshm/gollections/c" + s2 "github.com/m4gshm/gollections/internal/seq2" "github.com/m4gshm/gollections/map_/resolv" "github.com/m4gshm/gollections/op" - "golang.org/x/exp/constraints" + "github.com/m4gshm/gollections/seq" ) -// Seq is an alias of an iterator-function that allows to iterate over elements of a sequence, such as slice. -type Seq[V any] = func(yield func(V) bool) - -// Seq2 is an alias of an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. +// Seq2 is an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. // It is used to iterate over slice index/value pairs or map key/value pairs. -type Seq2[K, V any] = func(yield func(K, V) bool) +type Seq2[K, V any] = func(func(K, V) bool) -// Of creates an index/value pairs iterator over the elements. -func Of[T any](elements ...T) Seq2[int, T] { - return func(yield func(int, T) bool) { - for i, v := range elements { - if !yield(i, v) { +// Union combines several sequences into one. +func Union[S ~Seq2[K, V], K, V any](seq ...S) seq.Seq2[K, V] { + return s2.Union(seq...) +} + +// Of creates an key/value pairs iterator over the elements. +func Of[K, V any](elements ...c.KV[K, V]) seq.Seq2[K, V] { + return func(yield func(K, V) bool) { + for _, p := range elements { + if !yield(p.K, p.V) { break } } @@ -26,7 +32,7 @@ func Of[T any](elements ...T) Seq2[int, T] { } // OfMap creates an key/value pairs iterator over the elements map. -func OfMap[K comparable, V any](elements map[K]V) Seq2[K, V] { +func OfMap[K comparable, V any](elements map[K]V) seq.Seq2[K, V] { return func(yield func(K, V) bool) { for k, v := range elements { if !yield(k, v) { @@ -36,39 +42,48 @@ func OfMap[K comparable, V any](elements map[K]V) Seq2[K, V] { } } -// Union combines several sequences into one. -func Union[S ~Seq2[K, V], K, V any](seq ...S) Seq2[K, V] { - return func(yield func(K, V) bool) { - for _, s := range seq { - if s != nil { - for k, v := range s { - if !yield(k, v) { - return - } - } +// OfIndexed builds an indexed Seq2 iterator by extracting elements from an indexed soruce. +// the len is length ot the source. +// the getAt retrieves an element by its index from the source. +func OfIndexed[T any](amount int, getAt func(int) T) seq.Seq2[int, T] { + return func(yield func(int, T) bool) { + if getAt == nil { + return + } + for i := range amount { + if !yield(i, getAt(i)) { + break } } } } -// OfIndexed builds an indexed Seq2 iterator by extracting elements from an indexed soruce. +// OfIndexedKV builds an indexed Seq2 iterator by extracting key\value pairs from an indexed soruce. // the len is length ot the source. -// the getAt retrieves an element by its index from the source. -func OfIndexed[T any](amount int, getAt func(int) T) Seq2[int, T] { - return func(yield func(int, T) bool) { +// the getAt retrieves a key\value pair by its index from the source. +func OfIndexedKV[K, V any](amount int, getAt func(int) (K, V)) seq.Seq2[K, V] { + return func(yield func(K, V) bool) { if getAt == nil { return } for i := range amount { - if !yield(i, getAt(i)) { + if !yield(getAt(i)) { break } } } } +// OfIndexedPair builds an indexed Seq2 iterator by extracting key\value pairs from an indexed soruce. +// the len is length ot the source. +// the getKey retrieves a key by its index from the source. +// the getValue retrieves a value by its index from the source. +func OfIndexedPair[K, V any](amount int, getKey func(int) K, getValue func(int) V) seq.Seq2[K, V] { + return OfIndexedKV(amount, func(i int) (K, V) { return getKey(i), getValue(i) }) +} + // Series makes a sequence by applying the 'next' function to the previous step generated value. -func Series[T any](first T, next func(int, T) (T, bool)) Seq2[int, T] { +func Series[T any](first T, next func(int, T) (T, bool)) seq.Seq2[int, T] { return func(yield func(int, T) bool) { if next == nil { return @@ -93,7 +108,7 @@ func Series[T any](first T, next func(int, T) (T, bool)) Seq2[int, T] { } // RangeClosed creates a sequence that generates integers in the range defined by from and to inclusive -func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) Seq2[int, T] { +func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) seq.Seq2[int, T] { amount := toInclusive - from delta := T(1) if amount < 0 { @@ -112,8 +127,8 @@ func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) Seq2[int, } } -// Range creates a sequence that generates integers in the range defined by from and to exclusive -func Range[T constraints.Integer | rune](from T, toExclusive T) Seq2[int, T] { +// Range creates a sequence that generates integers in the range defined by from and to exclusive. +func Range[T constraints.Integer | rune](from T, toExclusive T) seq.Seq2[int, T] { amount := toExclusive - from delta := T(1) if amount < 0 { @@ -132,7 +147,7 @@ func Range[T constraints.Integer | rune](from T, toExclusive T) Seq2[int, T] { } // ToSeq converts an iterator of key/value pairs elements to an iterator of single elements by applying the 'converter' function to each iterable pair. -func ToSeq[S ~Seq2[K, V], T, K, V any](seq S, converter func(K, V) T) Seq[T] { +func ToSeq[S ~Seq2[K, V], T, K, V any](seq S, converter func(K, V) T) seq.Seq[T] { return func(yield func(T) bool) { if seq == nil || converter == nil { return @@ -160,8 +175,8 @@ func Top[S ~Seq2[K, V], K, V any](n int, seq S) S { } } -// Skip returns a sequence without first n elements. -func Skip[S ~Seq2[K, V], K, V any](n int, seq S) Seq2[K, V] { +// Skip returns the seq without first n elements. +func Skip[S ~Seq2[K, V], K, V any](n int, seq S) seq.Seq2[K, V] { return func(yield func(K, V) bool) { if seq == nil { return @@ -177,14 +192,14 @@ func Skip[S ~Seq2[K, V], K, V any](n int, seq S) Seq2[K, V] { } } -// While cuts tail elements of the seq that don't match the predicate. -func While[S ~Seq2[K, V], K, V any](seq S, predicate func(K, V) bool) Seq2[K, V] { +// While cuts tail elements of the seq that don't match the filter. +func While[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) seq.Seq2[K, V] { return func(yield func(K, V) bool) { if seq == nil { return } seq(func(k K, v V) bool { - if !predicate(k, v) { + if !filter(k, v) { return false } return yield(k, v) @@ -192,15 +207,15 @@ func While[S ~Seq2[K, V], K, V any](seq S, predicate func(K, V) bool) Seq2[K, V] } } -// SkipWhile returns a sequence without first elements of the seq that dont'math the predicate. -func SkipWhile[S ~Seq2[K, V], K, V any](seq S, predicate func(K, V) bool) Seq2[K, V] { +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func SkipWhile[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) seq.Seq2[K, V] { return func(yield func(K, V) bool) { if seq == nil { return } started := false seq(func(k K, v V) bool { - if !started && predicate(k, v) { + if !started && filter(k, v) { return true } started = true @@ -214,30 +229,23 @@ func Head[S ~Seq2[K, V], K, V any](seq S) (k K, v V, ok bool) { return First(seq, func(K, V) bool { return true }) } -// First returns the first key\value pair that satisfies the condition of the 'predicate' function. -func First[S ~Seq2[K, V], K, V any](seq S, predicate func(K, V) bool) (k K, v V, ok bool) { - if seq == nil || predicate == nil { - return - } - seq(func(oneK K, oneV V) bool { - if predicate(oneK, oneV) { - k = oneK - v = oneV - ok = true - return false - } - return true - }) - return +// First returns the first key\value pair that satisfies the condition. +func First[S ~Seq2[K, V], K, V any](seq S, condition func(K, V) bool) (k K, v V, ok bool) { + return s2.First(seq, condition) +} + +// Firstt returns the first key\value pair that satisfies the condition. +func Firstt[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) (bool, error)) (k K, v V, ok bool, err error) { + return s2.Firstt(seq, filter) } -// Reduce reduces the elements of the 'seq' sequence an one using the 'merge' function. +// Reduce reduces the elements of the seq into one using the 'merge' function. func Reduce[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) T) T { result, _ := ReduceOK(seq, merge) return result } -// ReduceOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). func ReduceOK[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) T) (result T, ok bool) { if seq == nil || merge == nil { @@ -252,13 +260,13 @@ func ReduceOK[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) T return result, started } -// Reducee reduces the elements of the 'seq' sequence an one using the 'merge' function. +// Reducee reduces the elements of the seq into one using the 'merge' function. func Reducee[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) (T, error)) (T, error) { result, _, err := ReduceeOK(seq, merge) return result, err } -// ReduceeOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). func ReduceeOK[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) (T, error)) (result T, ok bool, err error) { if seq == nil || merge == nil { @@ -273,120 +281,104 @@ func ReduceeOK[S ~Seq2[K, V], K, V, T any](seq S, merge func(prev *T, k K, v V) return result, started, err } -// Filter creates a rangefunc that iterates only those elements for which the 'filter' function returns true. -func Filter[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) Seq2[K, V] { - return func(yield func(K, V) bool) { - if seq == nil || filter == nil { - return - } - seq(func(k K, v V) bool { - if filter(k, v) { - return yield(k, v) - } - return true - }) - } +// HasAny checks whether the seq contains an element that satisfies the condition. +func HasAny[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) bool { + return s2.HasAny(seq, filter) } -// Convert creates a rangefunc that applies the 'converter' function to each iterable element. -func Convert[S ~Seq2[Kfrom, Vfrom], Kfrom, Vfrom, Kto, Vto any](seq S, converter func(Kfrom, Vfrom) (Kto, Vto)) Seq2[Kto, Vto] { - return func(consumer func(Kto, Vto) bool) { - if seq == nil || converter == nil { - return - } - seq(func(k Kfrom, v Vfrom) bool { - return consumer(converter(k, v)) - }) - } +// Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. +func Filter[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) bool) seq.Seq2[K, V] { + return s2.Filter(seq, filter) +} + +// Filt creates an erroreable iterator that iterates only those key\value pairs for which the 'filter' function returns true. +func Filt[S ~Seq2[K, V], K, V any](seq S, filter func(K, V) (bool, error)) seq.SeqE[c.KV[K, V]] { + return s2.Filt(seq, filter) +} + +// FilterKey returns a seq consisting of key/value pairs where the key satisfies the condition of the 'filter' function. +func FilterKey[S ~Seq2[K, V], K, V any](seq S, filter func(K) bool) seq.Seq2[K, V] { + return s2.FilterKey(seq, filter) +} + +// FilterValue returns a seq consisting of key/value pairs where the value satisfies the condition of the 'filter' function. +func FilterValue[S ~Seq2[K, V], K, V any](seq S, filter func(V) bool) seq.Seq2[K, V] { + return s2.FilterValue(seq, filter) +} + +// ConvertKey returns a seq that applies the 'converter' function to keys. +func ConvertKey[S ~Seq2[Kfrom, V], Kfrom, Kto, V any](seq S, converter func(Kfrom) Kto) seq.Seq2[Kto, V] { + return s2.ConvertKey(seq, converter) +} + +// ConvKey returns a seq that applies the 'converter' function to keys. +func ConvKey[S ~Seq2[Kfrom, V], Kfrom, Kto, V any](seq S, converter func(Kfrom) (Kto, error)) seq.SeqE[c.KV[Kto, V]] { + return s2.ConvKey(seq, converter) +} + +// ConvertValue returns a seq that applies the 'converter' function to values. +func ConvertValue[S ~Seq2[K, Vfrom], K, Vfrom, Vto any](seq S, converter func(Vfrom) Vto) seq.Seq2[K, Vto] { + return s2.ConvertValue(seq, converter) +} + +// ConvValue returns a seq that applies the 'converter' function to values. +func ConvValue[S ~Seq2[K, Vfrom], K, Vfrom, Vto any](seq S, converter func(Vfrom) (Vto, error)) seq.SeqE[c.KV[K, Vto]] { + return s2.ConvValue(seq, converter) +} + +// Convert creates an iterator that applies the 'converter' function to each iterable element. +func Convert[S ~Seq2[Kfrom, Vfrom], Kfrom, Vfrom, Kto, Vto any](seq S, converter func(Kfrom, Vfrom) (Kto, Vto)) seq.Seq2[Kto, Vto] { + return s2.Convert(seq, converter) +} + +// Conv creates an errorable seq that applies the 'converter' function to the iterable key\value pairs. +func Conv[S ~Seq2[Kfrom, Vfrom], Kfrom, Vfrom, Kto, Vto any](seq S, converter func(Kfrom, Vfrom) (Kto, Vto, error)) seq.SeqE[c.KV[Kto, Vto]] { + return s2.Conv(seq, converter) } // Values converts a key/value pairs iterator to an iterator of just values. -func Values[S ~Seq2[K, V], K, V any](seq S) Seq[V] { - return func(yield func(V) bool) { - if seq == nil { - return - } - seq(func(_ K, v V) bool { - return yield(v) - }) - } +func Values[S ~Seq2[K, V], K, V any](seq S) seq.Seq[V] { + return s2.Values(seq) } // Keys converts a key/value pairs iterator to an iterator of just keys. -func Keys[S ~Seq2[K, V], K, V any](seq S) Seq[K] { - return func(yield func(K) bool) { - if seq == nil { - return - } - seq(func(k K, _ V) bool { - return yield(k) - }) - } +func Keys[S ~Seq2[K, V], K, V any](seq S) seq.Seq[K] { + return s2.Keys(seq) } // Group collects the elements of the 'seq' sequence into a new map. func Group[S ~Seq2[K, V], K comparable, V any](seq S) map[K][]V { - return MapResolv(seq, resolv.Slice[K, V]) + return s2.Group(seq) } // Map collects key\value elements into a new map by iterating over the elements. func Map[S ~Seq2[K, V], K comparable, V any](seq S) map[K]V { - return MapResolv(seq, resolv.First[K, V]) + return s2.MapResolv(seq, resolv.First[K, V]) } // MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values. func MapResolv[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR) map[K]VR { - return AppendMapResolv(seq, resolver, nil) + return s2.MapResolv(seq, resolver) } // MapResolvOrder collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values. // Returns a slice with the keys ordered by the time they were added and the resolved key\value map. func MapResolvOrder[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR) ([]K, map[K]VR) { - return AppendMapResolvOrder(seq, resolver, nil, nil) + return s2.MapResolvOrder(seq, resolver) } // AppendMapResolv collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values. func AppendMapResolv[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR, dest map[K]VR) map[K]VR { - if seq == nil || resolver == nil { - return nil - } - if dest == nil { - dest = map[K]VR{} - } - seq(func(k K, v V) bool { - exists, ok := dest[k] - dest[k] = resolver(ok, k, exists, v) - return true - }) - return dest + return s2.AppendMapResolv(seq, resolver, dest) } // AppendMapResolvOrder collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values // Additionaly populates the 'order' slice by the keys ordered by the time they were added and the resolved key\value map. func AppendMapResolvOrder[S ~Seq2[K, V], K comparable, V, VR any](seq S, resolver func(exists bool, key K, valResolv VR, val V) VR, order []K, dest map[K]VR) ([]K, map[K]VR) { - if seq == nil || resolver == nil { - return nil, nil - } - if dest == nil { - dest = map[K]VR{} - } - seq(func(k K, v V) bool { - exists, ok := dest[k] - dest[k] = resolver(ok, k, exists, v) - if !ok { - order = append(order, k) - } - return true - }) - return order, dest + return s2.AppendMapResolvOrder(seq, resolver, order, dest) } // TrackEach applies the 'consumer' function to the seq elements. -func TrackEach[K, V any](seq Seq2[K, V], consumer func(K, V)) { - if seq == nil { - return - } - for k, v := range seq { - consumer(k, v) - } +func TrackEach[S ~Seq2[K, V], K, V any](seq S, consumer func(K, V)) { + s2.TrackEach(seq, consumer) } diff --git a/seq2/test/api_test.go b/seq2/test/api_test.go index ba2e7fd8..b2c8f89a 100644 --- a/seq2/test/api_test.go +++ b/seq2/test/api_test.go @@ -9,6 +9,7 @@ import ( "github.com/m4gshm/gollections/collection/immutable/ordered/map_" "github.com/m4gshm/gollections/k" kvpredicate "github.com/m4gshm/gollections/kv/predicate" + "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/predicate/eq" "github.com/m4gshm/gollections/predicate/less" "github.com/m4gshm/gollections/predicate/more" @@ -20,26 +21,29 @@ import ( "github.com/stretchr/testify/assert" ) +var errStop = errors.New("stop") +var even = func(i int, _ string) bool { return i%2 == 0 } + func Test_Of(t *testing.T) { - sequence := seq2.Of(0, 1, 2, 3, 4) - var out []int - var ind []int - for i, v := range sequence { - out = append(out, v) - ind = append(ind, i) + sequence := seq2.Of(k.V(0, "0"), k.V(1, "1"), k.V(2, "2"), k.V(3, "3"), k.V(4, "4")) + var values []string + var keys []int + for k, v := range sequence { + values = append(values, v) + keys = append(keys, k) } - assert.Equal(t, slice.Of(0, 1, 2, 3, 4), out) - assert.Equal(t, slice.Of(0, 1, 2, 3, 4), ind) - out = nil + assert.Equal(t, slice.Of("0", "1", "2", "3", "4"), values) + assert.Equal(t, slice.Of(0, 1, 2, 3, 4), keys) + values = nil for _, v := range sequence { - if v == 1 { + if v == "1" { break } - out = append(out, v) + values = append(values, v) } - assert.Equal(t, slice.Of(0), out) + assert.Equal(t, slice.Of("0"), values) - out = nil + values = nil var iter = false for _, v := range sequence { iter = true @@ -47,10 +51,10 @@ func Test_Of(t *testing.T) { break } assert.True(t, iter) - assert.Nil(t, out) + assert.Nil(t, values) } func Test_Union(t *testing.T) { - sequence := seq2.Union(seq2.Of(0, 1), nil, seq2.Of[int](), seq2.Of(2, 3, 4)) + sequence := seq2.Union(seq.Of2(0, 1), nil, seq.Of2[int](), seq.Of2(2, 3, 4)) assert.Equal(t, slice.Of(0, 1, 2, 3, 4), seq.Slice(seq2.Values(sequence))) assert.Equal(t, slice.Of(0, 1, 0, 1, 2), seq.Slice(seq2.Keys(sequence))) @@ -87,7 +91,7 @@ func Test_OfIndexed(t *testing.T) { } func Test_Series(t *testing.T) { - generator := func(i, prev int) (int, bool) { return prev + 1, prev < 3 } + generator := func(_, prev int) (int, bool) { return prev + 1, prev < 3 } sequence := seq2.Series(-1, generator) assert.Equal(t, slice.Of(-1, 0, 1, 2, 3), seq.Slice(seq2.Values(sequence))) assert.Equal(t, slice.Of(0, 1, 2, 3, 4), seq.Slice(seq2.Keys(sequence))) @@ -112,7 +116,7 @@ func Test_Series(t *testing.T) { } func Test_Map(t *testing.T) { - s := seq2.Of("first", "second", "third") + s := seq.Of2("first", "second", "third") m := seq2.Map(s) assert.Equal(t, "first", m[0]) @@ -121,7 +125,7 @@ func Test_Map(t *testing.T) { } func Test_Keys_Values(t *testing.T) { - s := seq2.Of("first", "second", "third") + s := seq.Of2("first", "second", "third") k := seq.Slice(seq2.Keys(s)) v := seq.Slice(seq2.Values(s)) assert.Equal(t, slice.Of(0, 1, 2), k) @@ -129,7 +133,7 @@ func Test_Keys_Values(t *testing.T) { } func Test_Group(t *testing.T) { - s := seq2.Convert(seq2.Of("first", "second", "third"), func(i int, s string) (bool, string) { return i%2 == 0, s }) + s := seq2.Convert(seq.Of2("first", "second", "third"), func(i int, s string) (bool, string) { return i%2 == 0, s }) m := seq2.Group(s) assert.Equal(t, slice.Of("first", "third"), sort.Asc(m[true])) @@ -146,21 +150,21 @@ func pairSum(prev *string, i int, val string) string { func Test_ReduceSum(t *testing.T) { - sum, ok := seq2.ReduceOK(seq2.Of("A", "B", "C"), pairSum) + sum, ok := seq2.ReduceOK(seq.Of2("A", "B", "C"), pairSum) assert.True(t, ok) assert.Equal(t, "0A1B2C", sum) } func Test_ReduceeSum(t *testing.T) { - s := seq2.Of(1, 3, 5, 7, 9, 11) - reducer := func(prev *int, i, v int) (int, error) { + s := seq.Of2(1, 3, 5, 7, 9, 11) + reducer := func(prev *int, _, v int) (int, error) { p := 0 if prev != nil { p = *prev } if v == 11 { - return p, errors.New("stop") + return p, errStop } return v + p, nil } @@ -169,7 +173,7 @@ func Test_ReduceeSum(t *testing.T) { assert.Equal(t, 1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") - _, ok, err = seq2.ReduceeOK[seq2.Seq2[int, int]](nil, reducer) + _, ok, err = seq2.ReduceeOK[seq.Seq2[int, int]](nil, reducer) assert.False(t, ok) assert.NoError(t, err) @@ -179,9 +183,9 @@ func Test_ReduceeSum(t *testing.T) { } func Test_ReduceeSumFirstErr(t *testing.T) { - s := seq2.Of(1, 3, 5, 7, 9, 11) + s := seq.Of2(1, 3, 5, 7, 9, 11) r, ok, err := seq2.ReduceeOK(s, func(_ *int, _, _ int) (int, error) { - return 0, errors.New("stop") + return 0, errStop }) assert.True(t, ok) assert.Equal(t, 0, r) @@ -189,7 +193,7 @@ func Test_ReduceeSumFirstErr(t *testing.T) { } func Test_ReduceEmpty(t *testing.T) { - s := seq2.Of[string]() + s := seq.Of2[string]() sum, ok := seq2.ReduceOK(s, pairSum) assert.False(t, ok) @@ -197,9 +201,12 @@ func Test_ReduceEmpty(t *testing.T) { } func Test_Head(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) _, result, ok := seq2.Head(sequence) + assert.True(t, ok) + assert.Equal(t, 1, result) + _, result, ok = sequence.Head() assert.True(t, ok) assert.Equal(t, 1, result) @@ -209,7 +216,7 @@ func Test_Head(t *testing.T) { } func Test_While(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) part := seq2.While(sequence, kvpredicate.Value[int](not.Eq(5))) assert.Equal(t, slice.Of(1, 2, 3, 4), seq.Slice(seq2.Values(part))) @@ -234,7 +241,7 @@ func Test_While(t *testing.T) { } func Test_SkipWhile(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) part := seq2.SkipWhile(sequence, kvpredicate.Value[int](less.Than(4))) assert.Equal(t, slice.Of(4, 5, 6), seq.Slice(seq2.Values(part))) @@ -256,7 +263,7 @@ func Test_SkipWhile(t *testing.T) { } func Test_Top(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) top := seq2.Values(seq2.Top(4, sequence)) result := seq.Slice(top) result2 := seq.Slice(top) @@ -270,7 +277,7 @@ func Test_Top(t *testing.T) { result = seq.Slice(seq2.Values(seq2.Top[seq.Seq2[int, int]](10, nil))) assert.Nil(t, result) result = nil - for _, v := range seq2.Top(4, seq2.Of(1, 2, 3, 4, 5, 6)) { + for _, v := range seq2.Top(4, seq.Of2(1, 2, 3, 4, 5, 6)) { if v != 3 { result = append(result, v) } else { @@ -281,7 +288,7 @@ func Test_Top(t *testing.T) { } func Test_Skip(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) skip := seq2.Values(seq2.Skip(4, sequence)) result := seq.Slice(skip) result2 := seq.Slice(skip) @@ -295,7 +302,7 @@ func Test_Skip(t *testing.T) { result = seq.Slice(seq2.Values(seq2.Skip[seq.Seq2[int, int]](10, nil))) assert.Nil(t, result) result = nil - for _, v := range seq2.Skip(2, seq2.Of(1, 2, 3, 4, 5, 6)) { + for _, v := range seq2.Skip(2, seq.Of2(1, 2, 3, 4, 5, 6)) { if v != 5 { result = append(result, v) } else { @@ -306,7 +313,7 @@ func Test_Skip(t *testing.T) { } func Test_SkipTop(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) middle := seq2.Top(2, seq2.Skip(2, sequence)) result := seq.Slice(seq2.Values(middle)) i := seq.Slice(seq2.Keys(middle)) @@ -316,13 +323,18 @@ func Test_SkipTop(t *testing.T) { } func Test_First(t *testing.T) { - sequence := seq2.Of(1, 2, 3, 4, 5, 6) + sequence := seq.Of2(1, 2, 3, 4, 5, 6) condition := func(_ int, v int) bool { return more.Than(5)(v) } i, result, ok := seq2.First(sequence, condition) + assert.True(t, ok) + assert.Equal(t, 5, i) + assert.Equal(t, 6, result) + i, result, ok = sequence.First(condition) assert.True(t, ok) assert.Equal(t, 5, i) assert.Equal(t, 6, result) + _, _, ok = seq2.First[seq.Seq2[int, int]](nil, condition) assert.False(t, ok) @@ -330,22 +342,102 @@ func Test_First(t *testing.T) { assert.False(t, ok) } +func Test_HasAny(t *testing.T) { + sequence := seq.Of2(1, 2, 3, 4, 5, 6) + mor5 := func(_, v int) bool { return more.Than(5)(v) } + ok := seq2.HasAny(sequence, mor5) + assert.True(t, ok) + + ok = sequence.HasAny(mor5) + assert.True(t, ok) +} + +func Test_Firstt(t *testing.T) { + sequence := seq.Of2(1, 2, 3, 4, 5, 6) + + condition := func(_ int, v int) (bool, error) { return more.Than(5)(v), nil } + i, result, ok, err := seq2.Firstt(sequence, condition) + + assert.True(t, ok) + assert.Equal(t, 5, i) + assert.Equal(t, 6, result) + assert.NoError(t, err) + + i, result, ok, err = sequence.Firstt(condition) + + assert.True(t, ok) + assert.Equal(t, 5, i) + assert.Equal(t, 6, result) + assert.NoError(t, err) + + _, _, ok, err = seq2.Firstt[seq.Seq2[int, int]](nil, condition) + assert.False(t, ok) + assert.NoError(t, err) + + _, _, ok, err = seq2.Firstt(sequence, nil) + assert.False(t, ok) + assert.NoError(t, err) + + _, _, ok, err = sequence.Firstt(nil) + assert.False(t, ok) + assert.NoError(t, err) + + conditionErr := func(_ int, v int) (bool, error) { + return more.Than(5)(v), op.IfElse(v > 3, errStop, nil) + } + i, result, ok, err = sequence.Firstt(conditionErr) + + assert.False(t, ok) + assert.Equal(t, 0, i) + assert.Equal(t, 0, result) + assert.Error(t, err) + + conditionErr = func(_ int, v int) (bool, error) { return more.Than(5)(v), op.IfElse(v > 5, errStop, nil) } + i, result, ok, err = sequence.Firstt(conditionErr) + + assert.True(t, ok) + assert.Equal(t, 5, i) + assert.Equal(t, 6, result) + assert.Error(t, err) +} + func Test_Filter(t *testing.T) { - s := seq2.Filter(seq2.Of("first", "second", "third"), func(i int, _ string) bool { return i%2 == 0 }) - k := seq.Slice(seq2.Keys(s)) - v := seq.Slice(seq2.Values(s)) + s := seq.Of2("first", "second", "third") + f := seq2.Filter(s, even) + k := seq.Slice(seq2.Keys(f)) + v := seq.Slice(seq2.Values(f)) assert.Equal(t, slice.Of(0, 2), k) assert.Equal(t, slice.Of("first", "third"), v) } +func Test_Filt(t *testing.T) { + s := seq.Of2("first", "second", "third", "fourth") + filter := func(i int, str string) (bool, error) { return even(i, str), op.IfElse(i > 2, errStop, nil) } + r, err := seq2.Filt(s, filter).Slice() + assert.Error(t, err) + assert.Equal(t, slice.Of(k.V(0, "first"), k.V(2, "third")), r) + + r, err = s.Filt(filter).Slice() + assert.Error(t, err) + assert.Equal(t, slice.Of(k.V(0, "first"), k.V(2, "third")), r) + + r, err = seq2.Filt(s, nil).Slice() + assert.NoError(t, err) + assert.Empty(t, r) + + r, err = seq2.Filt[seq.Seq2[int, string]](nil, filter).Slice() + assert.NoError(t, err) + assert.Empty(t, r) +} + var testMap = map_.Of(k.V(1, "10"), k.V(2, "20"), k.V(3, "30"), k.V(5, "50"), k.V(7, "70"), k.V(8, "80"), k.V(9, "90"), k.V(11, "110")) func Test_SeqOfNil(t *testing.T) { var in, out []int iter := false - for _, e := range seq2.Of(in...) { + for _, e := range seq.Of2(in...) { iter = true out = append(out, e) } @@ -380,8 +472,8 @@ func Test_OfMap(t *testing.T) { } func Test_ConvertNilSeq(t *testing.T) { - var in iter.Seq2[int, int] = nil - var out []int = nil + var in iter.Seq2[int, int] + var out []int iter := false for _, e := range seq2.Convert(in, func(i, e int) (int, int) { return i, e }) { @@ -393,26 +485,73 @@ func Test_ConvertNilSeq(t *testing.T) { assert.False(t, iter) } -func Test_AllFiltered(t *testing.T) { - s := []string{} +func Test_Convert(t *testing.T) { + i := []int{} - for _, v := range seq2.Filter(testMap.All, func(k int, _ string) bool { return k%2 == 0 }) { - s = append(s, v) + for _, e := range seq2.Convert(testMap.All, func(k int, v string) (int, int) { c, _ := strconv.Atoi(v); return k, c }) { + i = append(i, e) } - assert.Equal(t, slice.Of("20", "80"), sort.Asc(s)) + assert.Equal(t, slice.Of(10, 20, 30, 50, 70, 80, 90, 110), i) } -func Test_AllConverted(t *testing.T) { +func Test_ConvertValue(t *testing.T) { i := []int{} - for _, e := range seq2.Convert(testMap.All, func(k int, v string) (int, int) { c, _ := strconv.Atoi(v); return k, c }) { + for _, e := range seq2.ConvertValue(testMap.All, func(v string) int { c, _ := strconv.Atoi(v); return c }) { i = append(i, e) } assert.Equal(t, slice.Of(10, 20, 30, 50, 70, 80, 90, 110), i) } +func Test_ConvValue(t *testing.T) { + i := []int{} + + for kv, err := range seq2.ConvValue(testMap.All, strconv.Atoi) { + assert.NoError(t, err) + i = append(i, kv.V) + } + + assert.Equal(t, slice.Of(10, 20, 30, 50, 70, 80, 90, 110), i) +} + +func Test_ConvertKey(t *testing.T) { + i := []string{} + for k := range seq2.ConvertKey(testMap.All, strconv.Itoa) { + i = append(i, k) + } + assert.Equal(t, slice.Of("1", "2", "3", "5", "7", "8", "9", "11"), i) +} + +func Test_ConvKey(t *testing.T) { + i := []int{} + for kv, err := range seq2.ConvKey(seq2.ConvertKey(testMap.All, strconv.Itoa), strconv.Atoi) { + assert.NoError(t, err) + i = append(i, kv.K) + } + assert.Equal(t, slice.Of(1, 2, 3, 5, 7, 8, 9, 11), i) +} + +func Test_Conv(t *testing.T) { + i := []int{} + + for kv, err := range seq2.Conv(testMap.All, func(k int, v string) (int, int, error) { + if k == 5 { + return 0, 0, errStop + } + c, err := strconv.Atoi(v) + return k, c, err + }) { + if err != nil { + break + } + i = append(i, kv.V) + } + + assert.Equal(t, slice.Of(10, 20, 30), i) +} + func Test_Slice_ToMapResolvOrder(t *testing.T) { var ( even = func(v int) bool { return v%2 == 0 } @@ -466,7 +605,7 @@ func Test_RangeClosed(t *testing.T) { } func Test_ToSeq(t *testing.T) { - s := seq.Slice(seq2.ToSeq(seq2.Of("A", "B", "C"), func(i int, v string) string { return strconv.Itoa(i) + v })) + s := seq.Slice(seq2.ToSeq(seq.Of2("A", "B", "C"), func(i int, v string) string { return strconv.Itoa(i) + v })) assert.Equal(t, slice.Of("0A", "1B", "2C"), s) } diff --git a/seqe/api.go b/seqe/api.go index 92c941d9..0521776e 100644 --- a/seqe/api.go +++ b/seqe/api.go @@ -2,42 +2,37 @@ package seqe import ( - "github.com/m4gshm/gollections/c" + "github.com/m4gshm/gollections/convert" + "github.com/m4gshm/gollections/internal/seqe" "github.com/m4gshm/gollections/op" - "github.com/m4gshm/gollections/predicate/always" + "github.com/m4gshm/gollections/op/check/not" "github.com/m4gshm/gollections/seq" ) -// Seq is an alias of an iterator-function that allows to iterate over elements of a sequence, such as slice. -type Seq[T any] = seq.Seq[T] +// Seq is an iterator-function that allows to iterate over elements of a sequence, such as slice. +type Seq[T any] = func(func(T) bool) // SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. // It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. -type SeqE[T any] = seq.SeqE[T] - -// Seq2 is an alias of an iterator-function that allows to iterate over key/value pairs of a sequence, such as slice or map. -// It is used to iterate over slice index/value pairs or map key/value pairs. -type Seq2[K, V any] = seq.Seq2[K, V] +// At each iteration step, it is necessary to check for the occurrence of an error. +// +// for e, err := range seqence { +// if err != nil { +// break +// } +// ... +// } +type SeqE[T any] = func(func(T, error) bool) // Union combines several sequences into one. -func Union[S ~SeqE[T], T any](seq ...S) SeqE[T] { - return func(yield func(T, error) bool) { - for _, s := range seq { - if s != nil { - for v, err := range s { - if !yield(v, err) { - return - } - } - } - } - } +func Union[S ~SeqE[T], T any](seq ...S) seq.SeqE[T] { + return seqe.Union(seq...) } // OfNextGet builds an iterator by iterating elements of a source. // The hasNext specifies a predicate that tests existing of a next element in the source. // The getNext extracts the element. -func OfNextGet[T any](hasNext func() bool, getNext func() (T, error)) SeqE[T] { +func OfNextGet[T any](hasNext func() bool, getNext func() (T, error)) seq.SeqE[T] { return func(yield func(T, error) bool) { for hasNext() { if o, err := getNext(); !yield(o, err) { @@ -50,28 +45,28 @@ func OfNextGet[T any](hasNext func() bool, getNext func() (T, error)) SeqE[T] { // OfNext builds an iterator by iterating elements of a source. // The hasNext specifies a predicate that tests existing of a next element in the source. // The pushNext copy the element to the next pointer. -func OfNext[T any](hasNext func() bool, pushNext func(*T) error) SeqE[T] { +func OfNext[T any](hasNext func() bool, pushNext func(*T) error) seq.SeqE[T] { return OfNextGet(hasNext, func() (o T, err error) { return o, pushNext(&o) }) } // OfSourceNextGet builds an iterator by iterating elements of the source. // The hasNext specifies a predicate that tests existing of a next element in the source. // The getNext extracts the element. -func OfSourceNextGet[S, T any](source S, hasNext func(S) bool, getNext func(S) (T, error)) SeqE[T] { +func OfSourceNextGet[S, T any](source S, hasNext func(S) bool, getNext func(S) (T, error)) seq.SeqE[T] { return OfNextGet(func() bool { return hasNext(source) }, func() (T, error) { return getNext(source) }) } // OfSourceNext builds an iterator by iterating elements of the source. // The hasNext specifies a predicate that tests existing of a next element in the source. // The pushNext copy the element to the next pointer. -func OfSourceNext[S, T any](source S, hasNext func(S) bool, pushNext func(S, *T) error) SeqE[T] { +func OfSourceNext[S, T any](source S, hasNext func(S) bool, pushNext func(S, *T) error) seq.SeqE[T] { return OfNext(func() bool { return hasNext(source) }, func(next *T) error { return pushNext(source, next) }) } // OfIndexed builds a SeqE iterator by extracting elements from an indexed soruce. // the len is length ot the source. // the getAt retrieves an element by its index from the source. -func OfIndexed[T any](amount int, getAt func(int) (T, error)) Seq2[T, error] { +func OfIndexed[T any](amount int, getAt func(int) (T, error)) seq.SeqE[T] { return func(yield func(T, error) bool) { if getAt == nil { return @@ -86,219 +81,95 @@ func OfIndexed[T any](amount int, getAt func(int) (T, error)) Seq2[T, error] { } // Top returns a sequence of top n elements. -func Top[S ~SeqE[T], T any](n int, seq S) SeqE[T] { - return func(yield func(T, error) bool) { - if seq == nil { - return - } - m := n - seq(func(t T, err error) bool { - if m == 0 { - return false - } - m-- - return yield(t, err) - }) - } +func Top[S ~SeqE[T], T any](n int, seq S) seq.SeqE[T] { + return seqe.Top(n, seq) } -// Skip returns a sequence without first n elements. -func Skip[S ~SeqE[T], T any](n int, seq S) SeqE[T] { - return func(yield func(T, error) bool) { - if seq == nil { - return - } - m := n - seq(func(t T, err error) bool { - if m == 0 { - return yield(t, err) - } - m-- - return true - }) - } +// Skip returns the seq without first n elements. +func Skip[S ~SeqE[T], T any](n int, seq S) seq.SeqE[T] { + return seqe.Skip(n, seq) +} + +// While cuts tail elements of the seq that don't match the filter. +func While[S ~SeqE[T], T any](seq S, filter func(T) bool) seq.SeqE[T] { + return seqe.While(seq, filter) +} + +// SkipWhile returns a sequence without first elements of the seq that dont'math the filter. +func SkipWhile[S ~SeqE[T], T any](seq S, filter func(T) bool) seq.SeqE[T] { + return seqe.SkipWhile(seq, filter) } // Head returns the first element. func Head[S ~SeqE[T], T any](seq S) (v T, ok bool, err error) { - return First(seq, always.True) + return seqe.Head(seq) } -// First returns the first element that satisfies the condition of the 'predicate' function. -func First[S ~SeqE[T], T any](seq S, predicate func(T) bool) (v T, ok bool, err error) { - if seq == nil || predicate == nil { - return - } - seq(func(one T, e error) bool { - if e != nil { - err = e - ok = false - return false - } else if predicate(one) { - v = one - ok = true - return false - } - return true - }) - return +// First returns the first element that satisfies the condition. +func First[S ~SeqE[T], T any](seq S, condition func(T) bool) (v T, ok bool, err error) { + return seqe.First(seq, condition) } -// Firstt returns the first element that satisfies the condition of the 'predicate' function. -func Firstt[S ~SeqE[T], T any](seq S, predicate func(T) (bool, error)) (v T, ok bool, err error) { - if seq == nil || predicate == nil { - return v, false, nil - } - seq(func(one T, e error) bool { - if e != nil { - err = e - return false - } else if ok, err = predicate(one); ok { - v = one - return false - } else if err != nil { - return false - - } - return true - }) - return v, ok, err +// Firstt returns the first element that satisfies the condition. +func Firstt[S ~SeqE[T], T any](seq S, condition func(T) (bool, error)) (v T, ok bool, err error) { + return seqe.Firstt(seq, condition) } // Slice collects the elements of the 'seq' sequence into a new slice. func Slice[S ~SeqE[T], T any](seq S) ([]T, error) { - return SliceCap(seq, 0) + return seqe.Slice(seq) } // SliceCap collects the elements of the 'seq' sequence into a new slice with predefined capacity. func SliceCap[S ~SeqE[T], T any](seq S, capacity int) (out []T, e error) { - if seq == nil { - return nil, nil - } - if capacity > 0 { - out = make([]T, 0, capacity) - } - return Append(seq, out) + return seqe.SliceCap(seq, capacity) } // Append collects the elements of the 'seq' sequence into the specified 'out' slice. func Append[S ~SeqE[T], T any, TS ~[]T](seq S, out TS) (TS, error) { - if seq == nil { - return out, nil - } - var errOur error - seq(func(v T, e error) bool { - if e != nil { - errOur = e - return false - } - out = append(out, v) - return true - }) - return out, errOur + return seqe.Append(seq, out) } -// Reduce reduces the elements of the 'seq' sequence an one using the 'merge' function. +// Reduce reduces the elements of the seq into one using the 'merge' function. func Reduce[S ~SeqE[T], T any](seq S, merge func(T, T) T) (T, error) { - result, _, err := ReduceOK(seq, merge) - return result, err + return seqe.Reduce(seq, merge) } -// ReduceOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). func ReduceOK[S ~SeqE[T], T any](seq S, merge func(T, T) T) (result T, ok bool, err error) { - if seq == nil || merge == nil { - return result, false, nil - } - started := false - seq(func(v T, e error) bool { - if e != nil { - err = e - return false - } else if !started { - result = v - } else { - result = merge(result, v) - } - started = true - return true - }) - return result, started, err + return seqe.ReduceOK(seq, merge) } -// Reducee reduces the elements of the 'seq' sequence an one using the 'merge' function. +// Reducee reduces the elements of the seq into one using the 'merge' function. func Reducee[S ~SeqE[T], T any](seq S, merge func(T, T) (T, error)) (T, error) { - result, _, err := ReduceeOK(seq, merge) - return result, err + return seqe.Reducee(seq, merge) } -// ReduceeOK reduces the elements of the 'seq' sequence an one using the 'merge' function. +// ReduceeOK reduces the elements of the seq into one using the 'merge' function. // Returns ok==false if the seq returns ok=false at the first call (no more elements). func ReduceeOK[S ~SeqE[T], T any](seq S, merge func(T, T) (T, error)) (result T, ok bool, err error) { - if seq == nil || merge == nil { - return result, false, nil - } - started := false - seq(func(v T, e error) bool { - if e != nil { - err = e - return false - } else if !started { - result = v - } else { - result, err = merge(result, v) - if err != nil { - return false - } - } - started = true - return true - }) - return result, started, err + return seqe.ReduceeOK(seq, merge) } // 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 of the 'seq' sequence. -func Accum[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) T) (accumulator T, err error) { - accumulator = first - if seq == nil || merge == nil { - return - } - seq(func(v T, e error) bool { - err = e - if err != nil { - return false - } - accumulator = merge(accumulator, v) - return true - }) - return +func Accum[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) T) (T, error) { + return seqe.Accum(first, seq, merge) } // 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 of the 'seq' sequence. -func Accumm[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) (T, error)) (accumulator T, err error) { - accumulator = first - if seq == nil || merge == nil { - return accumulator, nil - } - seq(func(v T, e error) bool { - err = e - if err == nil { - accumulator, err = merge(accumulator, v) - } - return err == nil - }) - return accumulator, err +func Accumm[T any, S ~SeqE[T]](first T, seq S, merge func(T, T) (T, error)) (T, error) { + return seqe.Accumm(first, seq, merge) } // Sum returns the sum of all elements. -func Sum[S ~SeqE[T], T c.Summable](seq S) (out T, err error) { +func Sum[S ~SeqE[T], T op.Summable](seq S) (out T, err error) { return Accum(out, seq, op.Sum[T]) } -// HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful. -func HasAny[S ~SeqE[T], T any](seq S, predicate func(T) bool) (bool, error) { - _, ok, err := First(seq, predicate) - return ok, err +// HasAny checks whether the seq contains an element that satisfies the condition. +func HasAny[S ~SeqE[T], T any](seq S, condition func(T) bool) (bool, error) { + return seqe.HasAny(seq, condition) } // Contains finds the first element that equal to the example and returns true. @@ -317,51 +188,34 @@ func Contains[S ~SeqE[T], T comparable](seq S, example T) (contains bool, err er return } -// Conv creates an iterator that applies the 'converter' function to each iterable element and returns value-error pairs. +// Conv creates an errorable seq that applies the 'converter' function to the iterable elements. // The error should be checked at every iteration step, like: // // var integers iter.Seq2[int, error] // ... -// for s, err := range seqe.Conv(integers, strconv.Itoa) { +// for s, err := range seqe.Conv(integers, strconv.Itoa) { // if err != nil { // break // } // ... // } -func Conv[S ~SeqE[From], From, To any](seq S, converter func(From) (To, error)) SeqE[To] { - return func(yield func(To, error) bool) { - if seq == nil || converter == nil { - return - } - seq(func(from From, err error) bool { - if err != nil { - var to To - return yield(to, err) - } - return yield(converter(from)) - }) - } +func Conv[S ~SeqE[From], From, To any](seq S, converter func(From) (To, error)) seq.SeqE[To] { + return seqe.Conv(seq, converter) } // Convert creates an iterator that applies the 'converter' function to each iterable element. -func Convert[S ~SeqE[From], From, To any](seq S, converter func(From) To) SeqE[To] { - return func(yield func(To, error) bool) { - if seq == nil || converter == nil { - return - } - seq(func(from From, err error) bool { - if err != nil { - var to To - return yield(to, err) - } - return yield(converter(from), err) - }) - } +func Convert[S ~SeqE[From], From, To any](seq S, converter func(From) To) seq.SeqE[To] { + return seqe.Convert(seq, converter) +} + +// ConvertNilSafe creates a seq that filters not nil elements, converts that ones, filters not nils after converting and returns them. +func ConvertNilSafe[S ~SeqE[*From], From, To any](seq S, converter func(*From) *To) seq.SeqE[*To] { + return ConvertOK(seq, convert.NilSafe(converter)) } // ConvertOK creates an iterator that applies the 'converter' function to each iterable element. -// The converter may returns a value or ok=false to exclude the value from the loop. -func ConvertOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To, bool)) SeqE[To] { +// The converter may returns a value or ok=false to exclude the value from the sequence. +func ConvertOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To, bool)) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || converter == nil { return @@ -381,7 +235,7 @@ func ConvertOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To // ConvOK creates a iterator that applies the 'converter' function to each iterable element. // The converter may returns a value or ok=false to exclude the value from iteration. // It may also return an error to abort the iteration. -func ConvOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To, bool, error)) SeqE[To] { +func ConvOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To, bool, error)) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || converter == nil { return @@ -407,7 +261,7 @@ func ConvOK[S ~SeqE[From], From, To any](seq S, converter func(from From) (To, b // panic(err) // } // } -func Flat[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(From) STo) SeqE[To] { +func Flat[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(From) STo) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -439,7 +293,7 @@ func Flat[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(From // panic(err) // } // } -func FlatSeq[S ~SeqE[From], STo ~Seq[To], From any, To any](seq S, flattener func(From) STo) SeqE[To] { +func FlatSeq[S ~SeqE[From], STo ~Seq[To], From any, To any](seq S, flattener func(From) STo) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -478,7 +332,7 @@ func FlatSeq[S ~SeqE[From], STo ~Seq[To], From any, To any](seq S, flattener fun // } // ... // } -func Flatt[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(From) (STo, error)) SeqE[To] { +func Flatt[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(From) (STo, error)) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -520,7 +374,7 @@ func Flatt[S ~SeqE[From], STo ~[]To, From any, To any](seq S, flattener func(Fro // } // ... // } -func FlattSeq[S ~SeqE[From], STo ~SeqE[To], From any, To any](seq S, flattener func(From) STo) SeqE[To] { +func FlattSeq[S ~SeqE[From], STo ~SeqE[To], From any, To any](seq S, flattener func(From) STo) seq.SeqE[To] { return func(yield func(To, error) bool) { if seq == nil || flattener == nil { return @@ -543,36 +397,13 @@ func FlattSeq[S ~SeqE[From], STo ~SeqE[To], From any, To any](seq S, flattener f } // Filter creates an iterator that iterates only those elements for which the 'filter' function returns true. -func Filter[S ~SeqE[T], T any](seq S, filter func(T) bool) SeqE[T] { - return func(yield func(T, error) bool) { - if seq == nil || filter == nil { - return - } - seq(func(t T, err error) bool { - if err != nil || filter(t) { - return yield(t, err) - } - return true - }) - } +func Filter[S ~SeqE[T], T any](seq S, filter func(T) bool) seq.SeqE[T] { + return seqe.Filter(seq, filter) } // Filt creates an erroreable iterator that iterates only those elements for which the 'filter' function returns true. -func Filt[S ~SeqE[T], T any](seq S, filter func(T) (bool, error)) SeqE[T] { - return func(yield func(T, error) bool) { - if seq == nil || filter == nil { - return - } - seq(func(t T, err error) bool { - if err != nil { - return yield(t, err) - } - if ok, err := filter(t); ok || err != nil { - return yield(t, err) - } - return true - }) - } +func Filt[S ~SeqE[T], T any](seq S, filter func(T) (bool, error)) seq.SeqE[T] { + return seqe.Filt(seq, filter) } // Group collects the seq elements into a new map. @@ -594,16 +425,12 @@ func Group[S ~SeqE[T], T any, K comparable, V any](seq S, keyExtractor func(T) K return groups, nil } +// NotNil returns teh seq without nil elements. +func NotNil[T any](seq SeqE[*T]) SeqE[*T] { + return Filter(seq, not.Nil[T]) +} + // ForEach applies the 'consumer' function to the seq elements func ForEach[T any](seq SeqE[T], consumer func(T)) error { - if seq == nil { - return nil - } - for v, err := range seq { - if err != nil { - return err - } - consumer(v) - } - return nil + return seqe.ForEach(seq, consumer) } diff --git a/seqe/test/api_test.go b/seqe/test/api_test.go index 961619a0..effbc181 100644 --- a/seqe/test/api_test.go +++ b/seqe/test/api_test.go @@ -9,19 +9,24 @@ import ( "github.com/m4gshm/gollections/convert/as" "github.com/m4gshm/gollections/op" + "github.com/m4gshm/gollections/predicate/eq" + "github.com/m4gshm/gollections/predicate/less" "github.com/m4gshm/gollections/predicate/more" + "github.com/m4gshm/gollections/predicate/not" "github.com/m4gshm/gollections/seq" "github.com/m4gshm/gollections/seqe" "github.com/m4gshm/gollections/slice" - "github.com/m4gshm/gollections/slice/sort" "github.com/stretchr/testify/assert" ) +var errStop = errors.New("stop") +var even = func(v int) bool { return v%2 == 0 } + func noErr[T any](t T) (T, error) { return t, nil } func errOn[T comparable](errVal T) func(T) (T, error) { return func(val T) (T, error) { if val == errVal { - return val, errors.New("abort") + return val, errStop } return val, nil } @@ -30,7 +35,7 @@ func errOn[T comparable](errVal T) func(T) (T, error) { func errIfContains[T comparable](errVal T) func([]T) ([]T, error) { return func(val []T) ([]T, error) { if slice.Contains(val, errVal) { - return val, errors.New("abort") + return val, errStop } return val, nil } @@ -41,6 +46,11 @@ func Test_Union(t *testing.T) { result, err := seqe.Slice(sequence) assert.Equal(t, slice.Of(0, 1, 2), result) assert.Error(t, err) + + sequence2 := seq.Conv(seq.Of(0, 1), noErr).Union(nil, seq.ToSeq2(seq.Of[int](), errOn(0)).Union(seq.ToSeq2(seq.Of(2, 3, 4), errOn(3)))) + result, err = seqe.Slice(sequence2) + assert.Equal(t, slice.Of(0, 1, 2), result) + assert.Error(t, err) } func Test_OfIndexed(t *testing.T) { @@ -51,97 +61,165 @@ func Test_OfIndexed(t *testing.T) { assert.Equal(t, indexed, out) assert.NoError(t, err) - result = seqe.OfIndexed(len(indexed), func(i int) (string, error) { return indexed[i], op.IfElse(i == 3, errors.New("abort"), nil) }) + result = seqe.OfIndexed(len(indexed), func(i int) (string, error) { + return indexed[i], op.IfElse(i == 3, errStop, nil) + }) out, err = seqe.Slice(result) assert.Equal(t, slice.Of("0", "1", "2"), out) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") +} + +func Test_Append(t *testing.T) { + in := slice.Of(1) + out, err := seqe.Append[seq.SeqE[int]](nil, in) + assert.Equal(t, in, out) + assert.NoError(t, err) + + out, err = seqe.Append(seq.ToSeq2(seq.Of(2), noErr), out) + assert.Equal(t, []int{1, 2}, out) + assert.NoError(t, err) + + out, err = seq.Of(3).Conv(noErr).Append(out) + assert.Equal(t, []int{1, 2, 3}, out) + assert.NoError(t, err) + + out, err = seqe.Append(seq.Of(4, 5, 6).Conv(errOn(5)), out) + assert.Equal(t, []int{1, 2, 3, 4}, out) + assert.Error(t, err) + + out, err = seq.Of(7, 8, 9).Conv(errOn(8)).Append(out) + assert.Equal(t, []int{1, 2, 3, 4, 7}, out) + assert.Error(t, err) } func Test_AccumSum(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), noErr) + s := seq.Conv(seq.Of(1, 3, 5, 7, 9, 11), noErr) r, err := seqe.Accum(100, s, op.Sum[int]) assert.Equal(t, 100+1+3+5+7+9+11, r) assert.NoError(t, err) r, _ = seqe.Sum(s) assert.Equal(t, 1+3+5+7+9+11, r) + + r, _ = s.Accum(100, op.Sum[int]) + assert.Equal(t, 100+1+3+5+7+9+11, r) } func Test_AccummSum(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), noErr) - r, err := seqe.Accumm(100, s, func(i1, i2 int) (int, error) { + s := seq.Conv(seq.Of(1, 3, 5, 7, 9, 11), noErr) + adder := func(i1, i2 int) (int, error) { if i2 == 11 { - return i1, errors.New("stop") + return i1, errStop } return i1 + i2, nil - }) + } + r, err := seqe.Accumm(100, s, adder) + assert.Equal(t, 100+1+3+5+7+9, r) + assert.ErrorContains(t, err, "stop") + + r, err = s.Accumm(100, adder) assert.Equal(t, 100+1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") } func Test_ReduceSum(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr) - sum, ok, err := seqe.ReduceOK(s, op.Sum) + ns := seq.Of(1, 2, 3, 4, 5, 6) + s2 := seq.ToSeq2(ns, noErr) + sum, ok, err := seqe.ReduceOK(s2, op.Sum) assert.True(t, ok) assert.Equal(t, 21, sum) assert.NoError(t, err) - sum, err = seqe.Reduce(s, op.Sum) + sum, err = seqe.Reduce(s2, op.Sum) assert.Equal(t, 21, sum) assert.NoError(t, err) - s = seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(4)) - sum, ok, err = seqe.ReduceOK(s, op.Sum) + sum, err = seq.Conv(ns, noErr).Reduce(op.Sum) + assert.Equal(t, 21, sum) + assert.NoError(t, err) + + s2 = seq.ToSeq2(ns, errOn(4)) + sum, ok, err = seqe.ReduceOK(s2, op.Sum) + + assert.True(t, ok) + assert.Equal(t, 6, sum) + assert.ErrorContains(t, err, "stop") + + sum, ok, err = ns.Conv(errOn(4)).ReduceOK(op.Sum) assert.True(t, ok) assert.Equal(t, 6, sum) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") - s = seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(1)) - sum, ok, err = seqe.ReduceOK(s, op.Sum) + s2 = seq.ToSeq2(ns, errOn(1)) + sum, ok, err = seqe.ReduceOK(s2, op.Sum) assert.False(t, ok) assert.Equal(t, 0, sum) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") + + se := ns.Conv(errOn(1)) + sum, ok, err = se.ReduceOK(op.Sum) + + assert.False(t, ok) + assert.Equal(t, 0, sum) + assert.ErrorContains(t, err, "stop") } func Test_ReduceeSum(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), noErr) - r, ok, err := seqe.ReduceeOK(s, func(i1, i2 int) (int, error) { + s := seq.Of(1, 3, 5, 7, 9, 11) + s2 := seq.ToSeq2(s, noErr) + adderErr := func(i1, i2 int) (int, error) { if i2 == 11 { - return i1, errors.New("stop") + return i1, errStop } return i1 + i2, nil - }) + } + r, ok, err := seqe.ReduceeOK(s2, adderErr) assert.True(t, ok) assert.Equal(t, 1+3+5+7+9, r) assert.ErrorContains(t, err, "stop") - s = seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), errOn(5)) - r, ok, err = seqe.ReduceeOK(s, func(i1, i2 int) (int, error) { return i1 + i2, nil }) + r, ok, err = s.Conv(noErr).ReduceeOK(adderErr) + assert.True(t, ok) + assert.Equal(t, 1+3+5+7+9, r) + assert.ErrorContains(t, err, "stop") + + s2 = seq.ToSeq2(s, errOn(5)) + adder := func(i1, i2 int) (int, error) { return i1 + i2, nil } + r, ok, err = seqe.ReduceeOK(s2, adder) assert.True(t, ok) assert.Equal(t, 1+3, r) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") + + r, err = seqe.Reducee(s2, adder) + assert.Equal(t, 1+3, r) + assert.ErrorContains(t, err, "stop") - r, err = seqe.Reducee(s, func(i1, i2 int) (int, error) { return i1 + i2, nil }) + r, err = s.Conv(errOn(5)).Reducee(adder) assert.Equal(t, 1+3, r) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") + + s2 = seq.ToSeq2(s, errOn(1)) + r, ok, err = seqe.ReduceeOK(s2, adder) - s = seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), errOn(1)) - r, ok, err = seqe.ReduceeOK(s, func(i1, i2 int) (int, error) { return i1 + i2, nil }) + assert.False(t, ok) + assert.Equal(t, 0, r) + assert.ErrorContains(t, err, "stop") + r, ok, err = seq.Conv(s, errOn(1)).ReduceeOK(adder) assert.False(t, ok) assert.Equal(t, 0, r) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") } func Test_ReduceeSumFirstErr(t *testing.T) { s := seq.ToSeq2(seq.Of(1, 3, 5, 7, 9, 11), noErr) r, ok, err := seqe.ReduceeOK(s, func(_, _ int) (int, error) { - return 0, errors.New("stop") + return 0, errStop }) assert.True(t, ok) assert.Equal(t, 0, r) @@ -167,19 +245,100 @@ func Test_ReduceNil(t *testing.T) { } func Test_Head(t *testing.T) { - sequence := seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr) + sequence := seq.Conv(seq.Of(1, 2, 3, 4, 5, 6), noErr) result, ok, err := seqe.Head(sequence) assert.True(t, ok) assert.Equal(t, 1, result) assert.NoError(t, err) + result, ok, err = sequence.Head() + + assert.True(t, ok) + assert.Equal(t, 1, result) + assert.NoError(t, err) + result, ok, err = seqe.Head[seq.SeqE[int]](nil) assert.Zero(t, result) assert.False(t, ok) assert.NoError(t, err) } +func Test_While(t *testing.T) { + sequence := seq.SeqE[int](seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr)) + part := seqe.While(sequence, not.Eq(5)) + + s, err := seqe.Slice(part) + assert.Equal(t, slice.Of(1, 2, 3, 4), s) + assert.NoError(t, err) + + part = sequence.While(not.Eq(5)) + + s, err = seqe.Slice(part) + assert.Equal(t, slice.Of(1, 2, 3, 4), s) + assert.NoError(t, err) + + part = seqe.While(sequence, not.Eq(7)) + s, err = seqe.Slice(part) + assert.Equal(t, slice.Of(1, 2, 3, 4, 5, 6), s) + assert.NoError(t, err) + + part = seqe.While(sequence, eq.To(0)) + s, err = seqe.Slice(part) + assert.Nil(t, s) + assert.NoError(t, err) + + part = seqe.While[seq.SeqE[int]](nil, eq.To(0)) + s, err = seqe.Slice(part) + assert.Nil(t, s) + assert.NoError(t, err) + + sequence = seq.SeqE[int](seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(5))) + r := []int{} + for i, err := range sequence.While(not.Eq(7)) { + if err != nil { + break + } + r = append(r, i) + } + assert.Equal(t, slice.Of(1, 2, 3, 4), r) +} + +func Test_SkipWhile(t *testing.T) { + sequence := seq.SeqE[int](seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr)) + part := seqe.SkipWhile(sequence, less.Than(4)) + + s, err := seqe.Slice(part) + assert.Equal(t, slice.Of(4, 5, 6), s) + assert.NoError(t, err) + + part = sequence.SkipWhile(less.Than(4)) + + s, err = seqe.Slice(part) + assert.Equal(t, slice.Of(4, 5, 6), s) + assert.NoError(t, err) + + part = seqe.SkipWhile(sequence, not.Eq(7)) + s, err = seqe.Slice(part) + assert.Nil(t, s) + assert.NoError(t, err) + + part = seqe.SkipWhile(sequence, less.Than(0)) + s, err = seqe.Slice(part) + assert.Equal(t, slice.Of(1, 2, 3, 4, 5, 6), s) + assert.NoError(t, err) + + r := []int{} + sequence = seq.SeqE[int](seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(6))) + for i, err := range sequence.SkipWhile(less.Than(4)) { + if err != nil { + break + } + r = append(r, i) + } + assert.Equal(t, slice.Of(4, 5), r) +} + func Test_Top(t *testing.T) { sequence := seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr) top := seqe.Top(4, sequence) @@ -209,7 +368,7 @@ func Test_Top(t *testing.T) { assert.Equal(t, slice.Of(1, 2), result) result = nil - for v := range seqe.Top(4, seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(2))) { + for v := range seq.SeqE[int](seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(2))).Top(4) { if v != 2 { result = append(result, v) } @@ -218,14 +377,15 @@ func Test_Top(t *testing.T) { } func Test_Skip(t *testing.T) { - sequence := seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr) - skip := seqe.Skip(4, sequence) + s := seq.Of(1, 2, 3, 4, 5, 6) + s2 := seq.ToSeq2(s, noErr) + skip := seqe.Skip(4, s2) result, err := seqe.Slice(skip) assert.Equal(t, slice.Of(5, 6), result) assert.NoError(t, err) - result, err = seqe.Slice(seqe.Skip(0, sequence)) + result, err = seqe.Slice(seqe.Skip(0, s2)) assert.Equal(t, slice.Of(1, 2, 3, 4, 5, 6), result) assert.NoError(t, err) @@ -234,7 +394,7 @@ func Test_Skip(t *testing.T) { assert.NoError(t, err) result = nil - for v, err := range seqe.Skip(2, seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(5))) { + for v, err := range seqe.Skip(2, seq.ToSeq2(s, errOn(5))) { if v != 5 { result = append(result, v) assert.NoError(t, err, "unexpected error on %i", v) @@ -246,7 +406,15 @@ func Test_Skip(t *testing.T) { assert.Equal(t, slice.Of(3, 4), result) result = nil - for v := range seqe.Skip(2, seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(5))) { + for v := range seqe.Skip(2, seq.ToSeq2(s, errOn(5))) { + if v != 5 { + result = append(result, v) + } + } + assert.Equal(t, slice.Of(3, 4, 6), result) + + result = nil + for v := range seq.Conv(s, errOn(5)).Skip(2) { if v != 5 { result = append(result, v) } @@ -264,60 +432,80 @@ func Test_SkipTop(t *testing.T) { func Test_First(t *testing.T) { sequence := seq.Of(1, 2, 3, 4, 5, 6) - result, ok, err := seqe.First(seq.ToSeq2(sequence, noErr), more.Than(5)) + result, ok, err := seqe.First(seq.Conv(sequence, noErr), more.Than(5)) + + assert.True(t, ok) + assert.Equal(t, 6, result) + assert.NoError(t, err) + + result, ok, err = sequence.Conv(noErr).First(more.Than(5)) assert.True(t, ok) assert.Equal(t, 6, result) assert.NoError(t, err) - _, ok, err = seqe.First(seq.ToSeq2(sequence, errOn(1)), more.Than(5)) + _, ok, err = seqe.First(seq.Conv(sequence, errOn(1)), more.Than(5)) + assert.False(t, ok) + assert.ErrorContains(t, err, "stop") + _, ok, err = sequence.Conv(errOn(1)).First(more.Than(5)) assert.False(t, ok) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") +} + +func Test_HasAny(t *testing.T) { + sequence := seq.Of(1, 2, 3, 4, 5, 6) + ok, err := seqe.HasAny(seq.Conv(sequence, noErr), more.Than(5)) + assert.True(t, ok) + assert.NoError(t, err) - ok, err = seqe.HasAny(seq.ToSeq2(sequence, noErr), more.Than(5)) + ok, err = sequence.Conv(noErr).HasAny(more.Than(5)) assert.True(t, ok) assert.NoError(t, err) } func Test_Firstt(t *testing.T) { - sequence := seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), noErr) - result, ok, err := seqe.Firstt(sequence, func(i int) (bool, error) { - return more.Than(5)(i), nil - }) + firstErr := func(_ int) (bool, error) { return true, errStop } + mor5NoErr := func(i int) (bool, error) { return more.Than(5)(i), nil } + justErr := func(_ int) (bool, error) { return false, errStop } + s := seq.Of(1, 2, 3, 4, 5, 6) + s2 := seq.ToSeq2(s, noErr) + result, ok, err := seqe.Firstt(s2, mor5NoErr) assert.True(t, ok) assert.Equal(t, 6, result) assert.NoError(t, err) - result, ok, err = seqe.Firstt(sequence, func(_ int) (bool, error) { return true, errors.New("abort") }) + result, ok, err = seq.Conv(s, noErr).Firstt(mor5NoErr) + assert.True(t, ok) + assert.Equal(t, 6, result) + assert.NoError(t, err) + result, ok, err = seqe.Firstt(s2, firstErr) assert.True(t, ok) assert.Equal(t, 1, result) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") - result, ok, err = seqe.Firstt(sequence, func(_ int) (bool, error) { return false, errors.New("abort") }) + result, ok, err = seqe.Firstt(s2, justErr) assert.False(t, ok) assert.Equal(t, 0, result) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") - sequence = seq.ToSeq2(seq.Of(1, 2, 3, 4, 5, 6), errOn(1)) - result, ok, err = seqe.Firstt(sequence, func(i int) (bool, error) { return more.Than(5)(i), nil }) + s2 = seq.ToSeq2(s, errOn(1)) + result, ok, err = seqe.Firstt(s2, mor5NoErr) assert.False(t, ok) assert.Equal(t, 0, result) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") - _, ok, _ = seqe.Firstt(sequence, nil) + _, ok, _ = seqe.Firstt(s2, nil) assert.False(t, ok) - _, ok, _ = seqe.Firstt[seq.SeqE[int]](nil, func(_ int) (bool, error) { return false, errors.New("abort") }) + _, ok, _ = seqe.Firstt[seq.SeqE[int]](nil, justErr) assert.False(t, ok) } -var even = func(v int) bool { return v%2 == 0 } - func Test_Flat(t *testing.T) { md := seq.ToSeq2(seq.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), noErr) f := seqe.Flat(md, as.Is) @@ -341,7 +529,7 @@ func Test_Flat(t *testing.T) { f = seqe.Flat(md, as.Is) s, err = seqe.Slice(f) assert.Equal(t, []int{1, 2, 3, 4}, s) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") } func Test_FlatSeq(t *testing.T) { @@ -367,7 +555,7 @@ func Test_FlatSeq(t *testing.T) { f = seqe.FlatSeq(md, slices.Values) s, err = seqe.Slice(f) assert.Equal(t, []int{1, 2, 3, 4}, s) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") out = nil for i, err := range seqe.FlatSeq(md, func(i []int) seq.Seq[int] { @@ -406,7 +594,7 @@ func Test_Flatt(t *testing.T) { out, err = seqe.Slice(seqe.Flatt(s, f)) assert.Equal(t, []int{1, 2, 3, 4}, out) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") out = nil for v, err := range seqe.Flatt(s, f) { @@ -436,12 +624,12 @@ func Test_FlattSeq(t *testing.T) { assert.ErrorContains(t, err, "parsing \"_5\"") s = seq.ToSeq2(seq.Of([][]string{{"1", "2", "3"}, {"4"}, {"_5"}, {"6"}}...), func(s []string) ([]string, error) { - return s, op.IfElse(slice.Contains(s, "_5"), errors.New("abort"), nil) + return s, op.IfElse(slice.Contains(s, "_5"), errStop, nil) }) i, err = seqe.Slice(seqe.FlattSeq(s, f)) assert.Equal(t, []int{1, 2, 3, 4}, i) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") var out []int for v, err := range seqe.FlattSeq(s, f) { @@ -452,34 +640,50 @@ func Test_FlattSeq(t *testing.T) { } func Test_Filter(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), noErr) - f := seqe.Filter(s, even) - r, err := seqe.Slice(f) + s := seq.Of(1, 3, 4, 5, 7, 8, 9, 11) + s2 := seq.ToSeq2(s, noErr) + r, err := seqe.Filter(s2, even).Slice() assert.Equal(t, slice.Of(4, 8), r) assert.NoError(t, err) + + r, err = seq.Conv(s, noErr).Filter(even).Slice() + assert.Equal(t, slice.Of(4, 8), r) + assert.NoError(t, err) + } func Test_Filt(t *testing.T) { - s := seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), noErr) - filter := func(i int) (bool, error) { return even(i), op.IfElse(i > 7, errors.New("abort"), nil) } - l := seqe.Filt(s, filter) - r, err := seqe.Slice(l) + s := seq.Of(1, 3, 4, 5, 7, 8, 9, 11) + + s2 := seq.ToSeq2(s, noErr) + filter := func(i int) (bool, error) { return even(i), op.IfElse(i > 7, errStop, nil) } + r, err := seqe.Filt(s2, filter).Slice() + assert.Error(t, err) + assert.Equal(t, slice.Of(4), r) + + se := seq.Conv(s, noErr) + r, err = se.Filt(filter).Slice() assert.Error(t, err) assert.Equal(t, slice.Of(4), r) - l = seqe.Filt(s, nil) - r, err = seqe.Slice(l) + r, err = seqe.Filt(s2, nil).Slice() assert.NoError(t, err) assert.Empty(t, r) - l = seqe.Filt[seq.SeqE[int]](nil, filter) - r, err = seqe.Slice(l) + r, err = se.Filt(nil).Slice() assert.NoError(t, err) assert.Empty(t, r) - s = seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), errOn(4)) - l = seqe.Filt(s, filter) - r, err = seqe.Slice(l) + r, err = seqe.Filt[seq.SeqE[int]](nil, filter).Slice() + assert.NoError(t, err) + assert.Empty(t, r) + + s2 = seq.ToSeq2(s, errOn(4)) + r, err = seqe.Filt(s2, filter).Slice() + assert.Error(t, err) + assert.Nil(t, r) + + r, err = seq.Conv(s, errOn(4)).Filt(filter).Slice() assert.Error(t, err) assert.Nil(t, r) } @@ -488,7 +692,7 @@ func Test_Filt2(t *testing.T) { s := seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), noErr) l := seqe.Filt(s, func(i int) (bool, error) { ok := i <= 7 - return ok && even(i), op.IfElse(ok, nil, errors.New("abort")) + return ok && even(i), op.IfElse(ok, nil, errStop) }) r, err := seqe.Slice(l) assert.Error(t, err) @@ -508,7 +712,7 @@ func Test_Contains(t *testing.T) { s = seq.ToSeq2(seq.Of(1, 2, 3), errOn(1)) ok, err = seqe.Contains(s, 3) assert.False(t, ok) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") } type Rows[T any] struct { @@ -562,19 +766,7 @@ func Test_ConvertNilSeq(t *testing.T) { assert.False(t, iter) } -func Test_AllFiltered(t *testing.T) { - from := seq.Of(1, 2, 3, 5, 7, 8, 9, 11) - - s := []int{} - - for e := range seq.Filter(from, func(e int) bool { return e%2 == 0 }) { - s = append(s, e) - } - - assert.Equal(t, slice.Of(2, 8), sort.Asc(s)) -} - -func Test_AllConverted(t *testing.T) { +func Test_Convert(t *testing.T) { from := seq.ToSeq2(seq.Of(1, 2, 3, 5, 7, 8, 9, 11), noErr) s := []string{} @@ -620,6 +812,22 @@ func Test_Conv(t *testing.T) { assert.Equal(t, slice.Of(1, 2, 3, 5), out) } +func Test_ConvertNilSafe(t *testing.T) { + type entity struct{ val *string } + var ( + first = "first" + third = "third" + fifth = "fifth" + source = seq.ToSeq2(seq.Of([]*entity{{&first}, {}, {&third}, nil, {&fifth}}...), noErr) + result = seqe.ConvertNilSafe(source, func(e *entity) *string { return e.val }) + expected = []*string{&first, &third, &fifth} + ) + s, err := result.Slice() + + assert.NoError(t, err) + assert.Equal(t, expected, s) +} + func Test_ConvertOK(t *testing.T) { s := seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), noErr) converter := func(i int) (string, bool) { return strconv.Itoa(i), even(i) } @@ -641,7 +849,7 @@ func Test_ConvertOK(t *testing.T) { r = seqe.ConvertOK(s, converter) out, err = seqe.Slice(r) assert.Equal(t, []string{"4", "8"}, out) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") out = nil for s, err := range r { @@ -668,12 +876,12 @@ func Test_ConvOK(t *testing.T) { assert.Empty(t, o) r = seqe.ConvOK(s, func(i int) (string, bool, error) { - return strconv.Itoa(i), even(i), op.IfElse(i == 9, errors.New("abort"), nil) + return strconv.Itoa(i), even(i), op.IfElse(i == 9, errStop, nil) }) o, err = seqe.Slice(r) assert.Equal(t, []string{"4", "8"}, o) - assert.ErrorContains(t, err, "abort") + assert.ErrorContains(t, err, "stop") s = seq.ToSeq2(seq.Of(1, 3, 4, 5, 7, 8, 9, 11), errOn(5)) r = seqe.ConvOK(s, converter) @@ -683,8 +891,6 @@ func Test_ConvOK(t *testing.T) { } func Test_Group(t *testing.T) { - even := func(v int) bool { return v%2 == 0 } - groups, err := seqe.Group(seq.ToSeq2(seq.Of(1, 1, 2, 4, 3, 5), errOn(5)), even, as.Is) assert.Equal(t, slice.Of(2, 4), groups[true]) assert.Equal(t, slice.Of(1, 1, 3), groups[false]) @@ -698,8 +904,11 @@ func Test_Group(t *testing.T) { func Test_TrackEach(t *testing.T) { var out []int - seqe.ForEach(seq.ToSeq2(seq.RangeClosed(-1, 3), errOn(2)), func(v int) { - out = append(out, v) - }) + s2 := seq.ToSeq2(seq.RangeClosed(-1, 3), errOn(2)) + seqe.ForEach(s2, func(v int) { out = append(out, v) }) + assert.Equal(t, slice.Of(-1, 0, 1), out) + + out = nil + seq.Conv(seq.RangeClosed(-1, 3), errOn(2)).ForEach(func(v int) { out = append(out, v) }) assert.Equal(t, slice.Of(-1, 0, 1), out) } diff --git a/slice/api.go b/slice/api.go index 21264dfb..06fd580b 100644 --- a/slice/api.go +++ b/slice/api.go @@ -12,19 +12,26 @@ import ( "github.com/m4gshm/gollections/c" "github.com/m4gshm/gollections/comparer" "github.com/m4gshm/gollections/convert" - "github.com/m4gshm/gollections/loop" "github.com/m4gshm/gollections/map_/resolv" "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/op/check" "github.com/m4gshm/gollections/op/check/not" - "github.com/m4gshm/gollections/seq" ) -// Break is the 'break' statement of the For, Track methods -var Break = loop.Break +// Seq is an iterator-function that allows to iterate over elements of a sequence, such as slice. +type Seq[T any] = func(yield func(T) bool) -// Continue is an alias of the nil value used to continue iterating by For, Track methods. -var Continue = c.Continue +// SeqE is a specific iterator form that allows to retrieve a value with an error as second parameter of the iterator. +// It is used as a result of applying functions like seq.Conv, which may throw an error during iteration. +// At each iteration step, it is necessary to check for the occurrence of an error. +// +// for e, err := range seqence { +// if err != nil { +// break +// } +// ... +// } +type SeqE[T any] = func(yield func(T, error) bool) // Of is generic slice constructor func Of[T any](elements ...T) []T { return elements } @@ -34,15 +41,6 @@ func Len[TS ~[]T, T any](elements TS) int { return len(elements) } -// OfLoop builds a slice by iterating elements of a source. -// The hasNext specifies a predicate that tests existing of a next element in the source. -// The getNext extracts the element. -// -// Deprecated: renamed to OfNextGet. -func OfLoop[S, T any](source S, hasNext func(S) bool, getNext func(S) (T, error)) ([]T, error) { - return OfSourceNextGet(source, hasNext, getNext) -} - // OfNextGet builds a slice by iterating elements of a source. // The hasNext specifies a predicate that tests existing of a next element in the source. // The getNext extracts the element. @@ -222,6 +220,11 @@ func Convert[FS ~[]From, From, To any](elements FS, converter func(From) To) []T return result } +// ConvertNilSafe creates a slice that filters not nil elements, converts that ones, filters not nils after converting and returns them. +func ConvertNilSafe[FS ~[]*From, From, To any](elements FS, converter func(*From) *To) []*To { + return ConvertOK(elements, convert.NilSafe(converter)) +} + // Conv creates a slice consisting of the transformed elements using the converter. func Conv[FS ~[]From, From, To any](elements FS, converter func(From) (To, error)) ([]To, error) { if elements == nil || converter == nil { @@ -428,7 +431,7 @@ func Flat[FS ~[]From, From any, TS ~[]To, To any](elements FS, flattener func(Fr // // var arrays [][]int // var integers []int = slice.Flat(arrays, slices.Values) -func FlatSeq[FS ~[]From, From any, STo ~seq.Seq[To], To any](elements FS, flattener func(From) STo) []To { +func FlatSeq[FS ~[]From, From any, STo ~Seq[To], To any](elements FS, flattener func(From) STo) []To { if elements == nil || flattener == nil { return nil } @@ -468,7 +471,7 @@ func Flatt[FS ~[]From, From, To any](elements FS, flattener func(From) ([]To, er // var strings [][]string // var parse = func(f []string) ([]int, error) { ... } // integers, err := Flatt(strings, parse) -func FlattSeq[FS ~[]From, From any, STo ~seq.SeqE[To], To any](elements FS, flattener func(From) STo) ([]To, error) { +func FlattSeq[FS ~[]From, From any, STo ~SeqE[To], To any](elements FS, flattener func(From) STo) ([]To, error) { if elements == nil || flattener == nil { return nil, nil } @@ -576,13 +579,13 @@ func NotNil[TS ~[]*T, T any](elements TS) TS { // ToValues returns values referenced by the pointers. // If a pointer is nil then it is replaced by the zero value. func ToValues[TS ~[]*T, T any](pointers TS) []T { - return Convert(pointers, convert.PtrVal[T]) + return Convert(pointers, convert.ToVal[T]) } // GetValues returns values referenced by the pointers. // All nil pointers are excluded from the final result. func GetValues[TS ~[]*T, T any](elements TS) []T { - return ConvertOK(elements, convert.NoNilPtrVal[T]) + return ConvertOK(elements, convert.ToValNotNil[T]) } // Filter filters elements that match the filter condition and returns them. @@ -795,7 +798,7 @@ func Accumm[TS ~[]T, T any](first T, elements TS, merge func(T, T) (T, error)) ( } // Sum returns the sum of all elements -func Sum[TS ~[]T, T c.Summable](elements TS) (out T) { +func Sum[TS ~[]T, T op.Summable](elements TS) (out T) { return Accum(out, elements, op.Sum[T]) } @@ -891,18 +894,6 @@ func LasttI[TS ~[]T, T any](elements TS, by func(T) (bool, error)) (no T, index return no, -1, nil } -// Track applies the 'consumer' function to the elements until the consumer returns the c.Break to stop.tracking -func Track[TS ~[]T, T any](elements TS, consumer func(int, T) error) error { - for i, e := range elements { - if err := consumer(i, e); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - // TrackEach applies the 'consumer' function to the elements func TrackEach[TS ~[]T, T any](elements TS, consumer func(int, T)) { for i, e := range elements { @@ -910,31 +901,19 @@ func TrackEach[TS ~[]T, T any](elements TS, consumer func(int, T)) { } } -// TrackWhile applies the 'predicate' function to the elements while the fuction returns true. -func TrackWhile[TS ~[]T, T any](elements TS, predicate func(int, T) bool) { +// TrackWhile applies the 'filter' function to the elements while the fuction returns true. +func TrackWhile[TS ~[]T, T any](elements TS, filter func(int, T) bool) { for i, e := range elements { - if !predicate(i, e) { + if !filter(i, e) { break } } } -// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop. -func For[TS ~[]T, T any](elements TS, consumer func(T) error) error { - for _, e := range elements { - if err := consumer(e); err == Break { - return nil - } else if err != nil { - return err - } - } - return nil -} - -// WalkWhile applies the 'predicate' function for the elements until the predicate returns false to stop. -func WalkWhile[TS ~[]T, T any](elements TS, predicate func(T) bool) { +// WalkWhile applies the 'filter' function for the elements until the filter returns false to stop. +func WalkWhile[TS ~[]T, T any](elements TS, filter func(T) bool) { for _, e := range elements { - if !predicate(e) { + if !filter(e) { break } } @@ -1040,9 +1019,9 @@ func GetFilled[TS ~[]T, T any](elementsFactory func() TS, ifEmpty []T) TS { return Filled(elementsFactory(), ifEmpty) } -// HasAny tests if the 'elements' slice contains an element that satisfies the "predicate" condition -func HasAny[TS ~[]T, T any](elements TS, predicate func(T) bool) bool { - _, ok := First(elements, predicate) +// HasAny checks whether the elements contains an element that satisfies the condition. +func HasAny[TS ~[]T, T any](elements TS, condition func(T) bool) bool { + _, ok := First(elements, condition) return ok } diff --git a/slice/convert/api.go b/slice/convert/api.go index 1bc7b0e4..7f43cf98 100644 --- a/slice/convert/api.go +++ b/slice/convert/api.go @@ -33,14 +33,7 @@ func ToNotNil[FS ~[]From, From, To any](elements FS, converter func(From) *To) [ // NilSafe - convert.NilSafe filters not nil elements, converts that ones, filters not nils after converting and returns them func NilSafe[FS ~[]*From, From, To any](elements FS, converter func(*From) *To) []*To { - return slice.ConvertOK(elements, func(f *From) (*To, bool) { - if f != nil { - if t := converter(f); t != nil { - return t, true - } - } - return nil, false - }) + return slice.ConvertNilSafe(elements, converter) } // Check - convert.Check is a short alias of slice.ConvertOK diff --git a/slice/iter.go b/slice/iter.go index fe2ba776..9186a456 100644 --- a/slice/iter.go +++ b/slice/iter.go @@ -1,192 +1,5 @@ package slice -import ( - "unsafe" - - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/loop" - "github.com/m4gshm/gollections/notsafe" -) - -// IterNoStarted is the head Iterator position -const IterNoStarted = -1 - -// NewHead instantiates Iter based on elements slice -func NewHead[TS ~[]T, T any](elements TS) Iter[T] { - var ( - header = notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elements)) - array = unsafe.Pointer(header.Data) - size = header.Len - ) - return Iter[T]{ - array: array, - size: size, - current: IterNoStarted, - } -} - -// NewTail instantiates Iter based on elements slice for reverse iterating -func NewTail[T any](elements []T) Iter[T] { - var ( - header = notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elements)) - array = unsafe.Pointer(header.Data) - size = header.Len - ) - return Iter[T]{ - array: array, - size: size, - current: size, - } -} - -// Iter is the Iterator implementation. -type Iter[T any] struct { - array unsafe.Pointer - size, current int -} - -var ( - _ c.Iterator[any] = (*Iter[any])(nil) - _ c.PrevIterator[any] = (*Iter[any])(nil) -) - -func (i *Iter[T]) maxHasNext() int { - return i.size - 2 -} - -// All is used to iterate through the iterator using `for ... range`. -func (i *Iter[T]) All(consumer func(element T) bool) { - loop.All(i.Next, consumer) -} - -// For takes elements retrieved by the iterator. Can be interrupt by returning Break -func (i *Iter[T]) For(consumer func(element T) error) error { - return loop.For(i.Next, consumer) -} - -// ForEach takes all elements retrieved by the iterator. -func (i *Iter[T]) ForEach(consumer func(element T)) { - loop.ForEach(i.Next, consumer) -} - -// HasNext checks the next element existing -func (i *Iter[T]) HasNext() bool { - if i == nil { - return false - } - return CanIterateByRange(IterNoStarted, i.maxHasNext(), i.current) -} - -// HasPrev checks the previous element existing -func (i *Iter[T]) HasPrev() bool { - if i == nil { - return false - } - return CanIterateByRange(1, i.size, i.current) -} - -// GetNext returns the next element -func (i *Iter[T]) GetNext() T { - t, _ := i.Next() - return t -} - -// GetPrev returns the previous element -func (i *Iter[T]) GetPrev() T { - t, _ := i.Prev() - return t -} - -// Next returns the next element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *Iter[T]) Next() (v T, ok bool) { - if !(i == nil || i.array == nil) { - if current := i.current; CanIterateByRange(IterNoStarted, i.maxHasNext(), current) { - current++ - i.current = current - return *(*T)(notsafe.GetArrayElemRef(i.array, current, unsafe.Sizeof(v))), true - } - } - return v, ok -} - -// Prev returns the previos element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *Iter[T]) Prev() (v T, ok bool) { - if !(i == nil || i.array == nil) { - if current := i.current; CanIterateByRange(1, i.size, current) { - current-- - i.current = current - return *(*T)(notsafe.GetArrayElemRef(i.array, current, unsafe.Sizeof(v))), true - } - } - return v, ok -} - -// Get returns the current element. -// The ok result indicates whether the element was returned by the iterator. -// If ok == false, then the iteration must be completed. -func (i *Iter[T]) Get() (v T, ok bool) { - if !(i == nil || i.array == nil) { - current := i.current - if IsValidIndex(i.size, current) { - return *(*T)(notsafe.GetArrayElemRef(i.array, current, unsafe.Sizeof(v))), true - } - } - return v, ok -} - -// Size returns the iterator capacity -func (i *Iter[T]) Size() int { - if i == nil { - return 0 - } - return i.size -} - -// Crank rertieves a next element, returns the iterator, element and successfully flag. -func (i *Iter[T]) Crank() (it *Iter[T], t T, ok bool) { - if i != nil { - t, ok = i.Next() - } - return i, t, ok -} - -// CrankPrev rertieves a prev element, returns the iterator, element and successfully flag. -func (i *Iter[T]) CrankPrev() (it *Iter[T], t T, ok bool) { - if i != nil { - t, ok = i.Prev() - } - return i, t, ok -} - -// HasNext checks if an iterator can go forward -func HasNext[T any](elements []T, current int) bool { - return HasNextBySize(notsafe.GetLen(elements), current) -} - -// HasPrev checks if an iterator can go backwards -func HasPrev[T any](elements []T, current int) bool { - return HasPrevBySize(notsafe.GetLen(elements), current) -} - -// HasNextBySize checks if an iterator can go forward -func HasNextBySize(size int, current int) bool { - return CanIterateByRange(IterNoStarted, size-2, current) -} - -// HasPrevBySize checks if an iterator can go backwards -func HasPrevBySize(size, current int) bool { - return CanIterateByRange(1, size, current) -} - -// CanIterateByRange checks if an iterator can go further or stop -func CanIterateByRange(first, last, current int) bool { - return current >= first && current <= last -} - // IsValidIndex checks if index is out of range func IsValidIndex(size, index int) bool { return index > -1 && index < size diff --git a/slice/sum/api.go b/slice/sum/api.go index 4cfa634b..69f5758e 100644 --- a/slice/sum/api.go +++ b/slice/sum/api.go @@ -2,11 +2,11 @@ package sum import ( - "github.com/m4gshm/gollections/c" + "github.com/m4gshm/gollections/op" "github.com/m4gshm/gollections/slice" ) // Of an alias of the slice.Sum -func Of[TS ~[]T, T c.Summable](elements TS) T { +func Of[TS ~[]T, T op.Summable](elements TS) T { return slice.Sum(elements) } diff --git a/slice/test/api_test.go b/slice/test/api_test.go index 84ddb4e1..f2665b59 100644 --- a/slice/test/api_test.go +++ b/slice/test/api_test.go @@ -387,12 +387,12 @@ func Test_FlattSeq(t *testing.T) { transform := func(i int) (int, error) { return i, op.IfElse(i == 5, errors.New("abort"), nil) } - f, err := slice.FlattSeq(md, func(i []int) seq.SeqE[int] { return seq.ToSeq2(seq.Of(i...), transform) }) + f, err := slice.FlattSeq(md, func(i []int) seq.SeqE[int] { return seq.Conv(seq.Of(i...), transform) }) assert.Error(t, err) assert.Equal(t, []int{1, 2, 3, 4}, f) f, err = slice.FlattSeq(md, func(i []int) seq.SeqE[int] { - return seq.ToSeq2(seq.Of(i...), func(i int) (int, error) { return i, nil }) + return seq.SeqE[int](seq.ToSeq2(seq.Of(i...), func(i int) (int, error) { return i, nil })) }) assert.NoError(t, err) assert.Equal(t, []int{1, 2, 3, 4, 5, 6}, f) diff --git a/slice/test/slice_benchmark_test.go b/slice/test/slice_benchmark_test.go deleted file mode 100644 index bdc5dddd..00000000 --- a/slice/test/slice_benchmark_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package test - -import ( - "testing" - - "github.com/m4gshm/gollections/slice" -) - -func Benchmark_IsValidIndex(b *testing.B) { - for i := 0; i < b.N; i++ { - r := slice.IsValidIndex(5, 0) - r = slice.IsValidIndex(5, 5) - r = slice.IsValidIndex(5, -1) - _ = r - } -} - -func Benchmark_CanIterateByRange(b *testing.B) { - for i := 0; i < b.N; i++ { - r := slice.CanIterateByRange(slice.IterNoStarted, 5, 4) - r = slice.CanIterateByRange(slice.IterNoStarted, 5, 6) - r = slice.CanIterateByRange(slice.IterNoStarted, 5, slice.IterNoStarted) - _ = r - } -} diff --git a/walk/api.go b/walk/api.go deleted file mode 100644 index faa6f437..00000000 --- a/walk/api.go +++ /dev/null @@ -1,22 +0,0 @@ -// Package walk provides utilily functions for the interface Walker -package walk - -import ( - "github.com/m4gshm/gollections/c" -) - -// Group groups elements by keys into a new map -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.Group] -func Group[T any, K comparable, W c.ForEach[T]](elements W, by func(T) K) map[K][]T { - groups := map[K][]T{} - elements.ForEach(func(e T) { - key := by(e) - group := groups[key] - if group == nil { - group = make([]T, 0) - } - groups[key] = append(group, e) - }) - return groups -} diff --git a/walk/group/api.go b/walk/group/api.go deleted file mode 100644 index e7033053..00000000 --- a/walk/group/api.go +++ /dev/null @@ -1,14 +0,0 @@ -// Package group provides short aliases for functions that are used to group collection elements -package group - -import ( - "github.com/m4gshm/gollections/c" - "github.com/m4gshm/gollections/walk" -) - -// Of - group.Of synonym of the walk.Group. -// -// Deprecated: replaced by [github.com/m4gshm/gollections/seq.Group] -func Of[T any, K comparable, W c.ForEach[T]](elements W, by func(T) K) map[K][]T { - return walk.Group(elements, by) -} diff --git a/walk/group/test/api_test.go b/walk/group/test/api_test.go deleted file mode 100644 index bb401e83..00000000 --- a/walk/group/test/api_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package test - -import ( - "testing" - - "github.com/m4gshm/gollections/collection/immutable/vector" - "github.com/m4gshm/gollections/walk/group" - - "github.com/stretchr/testify/assert" -) - -func Test_group_odd_even(t *testing.T) { - var ( - even = func(v int) bool { return v%2 == 0 } - groups = group.Of(vector.Of(1, 1, 2, 4, 3, 1), even) - ) - assert.Equal(t, map[bool][]int{false: {1, 1, 3, 1}, true: {2, 4}}, groups) -}