github.com/m4gshm/gollections@v0.0.13-0.20240331203319-a34a86e58a24/break/kv/loop/api.go (about) 1 // Package loop provides helpers for loop operation over key/value pairs. 2 package loop 3 4 import ( 5 "errors" 6 7 "github.com/m4gshm/gollections/c" 8 "github.com/m4gshm/gollections/map_/resolv" 9 ) 10 11 // New is the mai breakable key/value loop constructor 12 func New[S, K, V any](source S, hasNext func(S) bool, getNext func(S) (K, V, error)) Loop[K, V] { 13 return func() (k K, v V, ok bool, err error) { 14 if ok := hasNext(source); !ok { 15 return k, v, false, nil 16 } else if k, v, err = getNext(source); err != nil { 17 return k, v, false, err 18 } else { 19 return k, v, true, nil 20 } 21 } 22 } 23 24 // From wrap the next loop to a breakable loop 25 func From[K, V any](next func() (K, V, bool)) func() (K, V, bool, error) { 26 return func() (K, V, bool, error) { 27 k, v, ok := next() 28 return k, v, ok, nil 29 } 30 } 31 32 // To transforms a breakable loop to a simple loop. 33 // The errConsumer is a function that is called when an error occurs. 34 func To[K, V any](next func() (K, V, bool, error), errConsumer func(error)) func() (K, V, bool) { 35 return func() (K, V, bool) { 36 k, v, ok, err := next() 37 if err != nil { 38 errConsumer(err) 39 return k, v, false 40 } 41 return k, v, ok 42 } 43 } 44 45 // Group collects sets of values grouped by keys obtained by passing a key/value loop. 46 func Group[K comparable, V any](next func() (K, V, bool, error)) (map[K][]V, error) { 47 return ToMapResolv(next, resolv.Slice[K, V]) 48 } 49 50 // Reduce reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function 51 func Reduce[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V)) (rk K, rv V, err error) { 52 if next == nil { 53 return rk, rv, nil 54 } 55 k, v, ok, err := next() 56 if err != nil || !ok { 57 return rk, rv, err 58 } 59 rk, rv = k, v 60 for { 61 k, v, ok, err := next() 62 if err != nil || !ok { 63 return rk, rv, err 64 } 65 rk, rv = merge(rk, k, rv, v) 66 } 67 } 68 69 // Reducee reduces the key/value pairs retrieved by the 'next' function into an one pair using the 'merge' function 70 func Reducee[K, V any](next func() (K, V, bool, error), merge func(K, K, V, V) (K, V, error)) (rk K, rv V, err error) { 71 if next == nil { 72 return rk, rv, nil 73 } 74 k, v, ok, err := next() 75 if err != nil || !ok { 76 return rk, rv, err 77 } 78 rk, rv = k, v 79 for { 80 if k, v, ok, err := next(); err != nil || !ok { 81 return rk, rv, err 82 } else if rk, rv, err = merge(rk, k, rv, v); err != nil { 83 return rk, rv, err 84 } 85 } 86 } 87 88 // HasAny finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful 89 func HasAny[K, V any](next func() (K, V, bool, error), predicate func(K, V) bool) (bool, error) { 90 _, _, ok, err := First(next, predicate) 91 return ok, err 92 } 93 94 // HasAnyy finds the first key/value pair that satisfies the 'predicate' function condition and returns true if successful 95 func HasAnyy[K, V any](next func() (K, V, bool, error), predicate func(K, V) (bool, error)) (bool, error) { 96 _, _, ok, err := Firstt(next, predicate) 97 return ok, err 98 } 99 100 // First returns the first key/value pair that satisfies the condition of the 'predicate' function 101 func First[K, V any](next func() (K, V, bool, error), predicate func(K, V) bool) (K, V, bool, error) { 102 for { 103 if k, v, ok, err := next(); err != nil || !ok { 104 return k, v, false, err 105 } else if ok := predicate(k, v); ok { 106 return k, v, true, nil 107 } 108 } 109 } 110 111 // Firstt returns the first key/value pair that satisfies the condition of the 'predicate' function 112 func Firstt[K, V any](next func() (K, V, bool, error), predicate func(K, V) (bool, error)) (K, V, bool, error) { 113 for { 114 if k, v, ok, err := next(); err != nil || !ok { 115 return k, v, false, err 116 } else if ok, err := predicate(k, v); err != nil || ok { 117 return k, v, ok, err 118 } 119 } 120 } 121 122 // Convert creates a loop that applies the 'converter' function to iterable key\values. 123 func Convert[K, V any, KOUT, VOUT any](next func() (K, V, bool, error), converter func(K, V) (KOUT, VOUT)) Loop[KOUT, VOUT] { 124 if next == nil { 125 return nil 126 } 127 return func() (k2 KOUT, v2 VOUT, ok bool, err error) { 128 k, v, ok, err := next() 129 if err != nil || !ok { 130 return k2, v2, false, err 131 } 132 k2, v2 = converter(k, v) 133 return k2, v2, true, nil 134 } 135 } 136 137 // Conv creates a loop that applies the 'converter' function to iterable key\values. 138 func Conv[K, V any, KOUT, VOUT any](next func() (K, V, bool, error), converter func(K, V) (KOUT, VOUT, error)) Loop[KOUT, VOUT] { 139 if next == nil { 140 return nil 141 } 142 return func() (k2 KOUT, v2 VOUT, ok bool, err error) { 143 k, v, ok, err := next() 144 if err != nil || !ok { 145 return k2, v2, false, err 146 } 147 k2, v2, err = converter(k, v) 148 return k2, v2, err == nil, err 149 } 150 } 151 152 // Filter creates a loop that checks elements by the 'filter' function and returns successful ones. 153 func Filter[K, V any](next func() (K, V, bool, error), filter func(K, V) bool) Loop[K, V] { 154 if next == nil { 155 return nil 156 } 157 return func() (K, V, bool, error) { 158 return First(next, filter) 159 } 160 } 161 162 // Filt creates a loop that checks elements by the 'filter' function and returns successful ones. 163 func Filt[K, V any](next func() (K, V, bool, error), filter func(K, V) (bool, error)) Loop[K, V] { 164 if next == nil { 165 return nil 166 } 167 return func() (K, V, bool, error) { 168 return Firstt(next, filter) 169 } 170 } 171 172 // ToMapResolv collects key\value elements to a map by iterating over the elements with resolving of duplicated key values 173 func ToMapResolv[K comparable, V, VR any](next func() (K, V, bool, error), resolver func(bool, K, VR, V) VR) (map[K]VR, error) { 174 m := map[K]VR{} 175 for { 176 k, v, ok, err := next() 177 if err != nil || !ok { 178 return m, err 179 } 180 exists, ok := m[k] 181 m[k] = resolver(ok, k, exists, v) 182 } 183 } 184 185 // ToMap collects key\value elements to a map by iterating over the elements 186 func ToMap[K comparable, V any](next func() (K, V, bool, error)) (map[K]V, error) { 187 return ToMapResolv(next, resolv.First[K, V]) 188 } 189 190 // ToSlice collects key\value elements to a slice by iterating over the elements 191 func ToSlice[K, V, T any](next func() (K, V, bool, error), converter func(K, V) T) ([]T, error) { 192 if next == nil { 193 return nil, nil 194 } 195 s := []T{} 196 for { 197 key, val, ok, err := next() 198 if ok { 199 s = append(s, converter(key, val)) 200 } 201 if !ok || err != nil { 202 return s, err 203 } 204 } 205 } 206 207 // Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop. 208 func Track[K, V any](next func() (K, V, bool, error), consumer func(K, V) error) error { 209 if next == nil { 210 return nil 211 } 212 for { 213 if p, v, ok, err := next(); err != nil || !ok { 214 return err 215 } else if err := consumer(p, v); err != nil { 216 return brk(err) 217 } 218 } 219 } 220 221 // Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. 222 func Crank[K, V any](next func() (K, V, bool, error)) (n Loop[K, V], k K, v V, ok bool, err error) { 223 if next != nil { 224 k, v, ok, err = next() 225 } 226 return next, k, v, ok, err 227 } 228 229 func brk(err error) error { 230 if errors.Is(err, c.Break) { 231 return nil 232 } 233 return err 234 }