github.com/m4gshm/gollections@v0.0.13-0.20240331203319-a34a86e58a24/loop/api.go (about) 1 // Package loop provides helpers for loop operation and iterator implementations 2 package loop 3 4 import ( 5 "unsafe" 6 7 "golang.org/x/exp/constraints" 8 9 breakkvloop "github.com/m4gshm/gollections/break/kv/loop" 10 breakloop "github.com/m4gshm/gollections/break/loop" 11 breakAlways "github.com/m4gshm/gollections/break/predicate/always" 12 "github.com/m4gshm/gollections/c" 13 "github.com/m4gshm/gollections/convert" 14 "github.com/m4gshm/gollections/convert/as" 15 kvloop "github.com/m4gshm/gollections/kv/loop" 16 "github.com/m4gshm/gollections/map_/resolv" 17 "github.com/m4gshm/gollections/notsafe" 18 "github.com/m4gshm/gollections/op" 19 "github.com/m4gshm/gollections/op/check/not" 20 "github.com/m4gshm/gollections/predicate/always" 21 ) 22 23 // Break is the 'break' statement of the For, Track methods. 24 var Break = c.Break 25 26 // Continue is an alias of the nil value used to continue iterating by For, Track methods. 27 var Continue = c.Continue 28 29 // S wrap the elements by loop function. 30 func S[TS ~[]T, T any](elements TS) Loop[T] { 31 return Of(elements...) 32 } 33 34 // Of wrap the elements by loop function. 35 func Of[T any](elements ...T) Loop[T] { 36 l := len(elements) 37 i := 0 38 if l == 0 { 39 return nil 40 } 41 return func() (e T, ok bool) { 42 if i < l { 43 e, ok = elements[i], true 44 i++ 45 } 46 return e, ok 47 } 48 } 49 50 // All is an adapter for the next function for iterating by `for ... range`. Supported since go 1.22 with GOEXPERIMENT=rangefunc enabled. 51 func All[T any](next func() (T, bool), consumer func(T) bool) { 52 if next == nil { 53 return 54 } 55 for v, ok := next(); ok && consumer(v); v, ok = next() { 56 } 57 } 58 59 // New makes a loop from an abstract source 60 func New[S, T any](source S, hasNext func(S) bool, getNext func(S) T) Loop[T] { 61 return func() (out T, ok bool) { 62 if hasNext(source) { 63 out, ok = getNext(source), true 64 } 65 return out, ok 66 } 67 } 68 69 // For applies the 'consumer' function for the elements retrieved by the 'next' function until the consumer returns the c.Break to stop. 70 func For[T any](next func() (T, bool), consumer func(T) error) error { 71 if next == nil { 72 return nil 73 } 74 for v, ok := next(); ok; v, ok = next() { 75 if err := consumer(v); err == Break { 76 return nil 77 } else if err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 84 // ForEach applies the 'consumer' function to the elements retrieved by the 'next' function 85 func ForEach[T any](next func() (T, bool), consumer func(T)) { 86 if next == nil { 87 return 88 } 89 for v, ok := next(); ok; v, ok = next() { 90 consumer(v) 91 } 92 } 93 94 // ForEachFiltered applies the 'consumer' function to the elements retrieved by the 'next' function that satisfy the 'predicate' function condition 95 func ForEachFiltered[T any](next func() (T, bool), predicate func(T) bool, consumer func(T)) { 96 if next == nil { 97 return 98 } 99 for v, ok := next(); ok; v, ok = next() { 100 if predicate(v) { 101 consumer(v) 102 } 103 } 104 } 105 106 // First returns the first element that satisfies the condition of the 'predicate' function 107 func First[T any](next func() (T, bool), predicate func(T) bool) (v T, ok bool) { 108 if next == nil { 109 return v, false 110 } 111 for one, ok := next(); ok; one, ok = next() { 112 if predicate(one) { 113 return one, true 114 } 115 } 116 return v, ok 117 } 118 119 // Firstt returns the first element that satisfies the condition of the 'predicate' function 120 func Firstt[T any](next func() (T, bool), predicate func(T) (bool, error)) (v T, ok bool, err error) { 121 if next == nil { 122 return v, false, nil 123 } 124 for { 125 if out, ok := next(); !ok { 126 return out, false, nil 127 } else if ok, err := predicate(out); err != nil || ok { 128 return out, ok, err 129 } 130 } 131 } 132 133 // Track applies the 'consumer' function to position/element pairs retrieved by the 'next' function until the consumer returns the c.Break to stop.tracking. 134 func Track[I, T any](next func() (I, T, bool), consumer func(I, T) error) error { 135 return kvloop.Track(next, consumer) 136 } 137 138 // TrackEach applies the 'consumer' function to position/element pairs retrieved by the 'next' function 139 func TrackEach[I, T any](next func() (I, T, bool), consumer func(I, T)) { 140 kvloop.TrackEach(next, consumer) 141 } 142 143 // Slice collects the elements retrieved by the 'next' function into a new slice 144 func Slice[T any](next func() (T, bool)) []T { 145 return SliceCap(next, 0) 146 } 147 148 // SliceCap collects the elements retrieved by the 'next' function into a new slice with predefined capacity 149 func SliceCap[T any](next func() (T, bool), cap int) (out []T) { 150 if next == nil { 151 return nil 152 } 153 if cap > 0 { 154 out = make([]T, 0, cap) 155 } 156 return Append(next, out) 157 } 158 159 // Append collects the elements retrieved by the 'next' function into the specified 'out' slice 160 func Append[T any, TS ~[]T](next func() (T, bool), out TS) TS { 161 if next == nil { 162 return nil 163 } 164 for { 165 v, ok := next() 166 if !ok { 167 break 168 } 169 out = append(out, v) 170 } 171 return out 172 } 173 174 // Reduce reduces the elements retrieved by the 'next' function into an one using the 'merge' function. 175 func Reduce[T any](next func() (T, bool), merge func(T, T) T) (result T) { 176 if next == nil { 177 return result 178 } 179 if v, ok := next(); ok { 180 result = v 181 } else { 182 return result 183 } 184 for v, ok := next(); ok; v, ok = next() { 185 result = merge(result, v) 186 } 187 return result 188 } 189 190 // Reducee reduces the elements retrieved by the 'next' function into an one pair using the 'merge' function. 191 func Reducee[T any](next func() (T, bool), merge func(T, T) (T, error)) (result T, err error) { 192 if next == nil { 193 return result, nil 194 } 195 if v, ok := next(); ok { 196 result = v 197 } else { 198 return result, nil 199 } 200 for v, ok := next(); ok; v, ok = next() { 201 result, err = merge(result, v) 202 if err != nil { 203 return result, err 204 } 205 } 206 return result, nil 207 } 208 209 // Sum returns the sum of all elements 210 func Sum[T c.Summable](next func() (T, bool)) T { 211 return Reduce(next, op.Sum[T]) 212 } 213 214 // HasAny finds the first element that satisfies the 'predicate' function condition and returns true if successful 215 func HasAny[T any](next func() (T, bool), predicate func(T) bool) bool { 216 _, ok := First(next, predicate) 217 return ok 218 } 219 220 // Contains finds the first element that equal to the example and returns true 221 func Contains[T comparable](next func() (T, bool), example T) bool { 222 if next == nil { 223 return false 224 } 225 for one, ok := next(); ok; one, ok = next() { 226 if one == example { 227 return true 228 } 229 } 230 return false 231 } 232 233 // Conv creates a loop that applies the 'converter' function to iterable elements. 234 func Conv[From, To any](next func() (From, bool), converter func(From) (To, error)) breakloop.Loop[To] { 235 return breakloop.Conv(breakloop.From(next), converter) 236 } 237 238 // ConvS creates a loop that applies the 'converter' function to the 'elements' slice. 239 func ConvS[FS ~[]From, From, To any](elements FS, converter func(From) (To, error)) breakloop.Loop[To] { 240 return Conv(S(elements), converter) 241 } 242 243 // Convert creates a loop that applies the 'converter' function to iterable elements. 244 func Convert[From, To any](next func() (From, bool), converter func(From) To) Loop[To] { 245 if next == nil { 246 return nil 247 } 248 return func() (t To, ok bool) { 249 v, ok := next() 250 if ok { 251 return converter(v), true 252 } 253 return t, false 254 } 255 } 256 257 // ConvertS creates a loop that applies the 'converter' function to the 'elements' slice. 258 func ConvertS[FS ~[]From, From, To any](elements FS, converter func(From) To) Loop[To] { 259 return Convert(S(elements), converter) 260 } 261 262 // ConvCheck is similar to ConvertFilt, but it checks and transforms elements together 263 func ConvCheck[From, To any](next func() (From, bool), converter func(from From) (To, bool, error)) breakloop.Loop[To] { 264 return breakloop.ConvCheck(breakloop.From(next), converter) 265 } 266 267 // ConvertCheck is similar to ConvertFilt, but it checks and transforms elements together 268 func ConvertCheck[From, To any](next func() (From, bool), converter func(from From) (To, bool)) Loop[To] { 269 if next == nil { 270 return nil 271 } 272 return func() (t To, ok bool) { 273 for e, ok := next(); ok; e, ok = next() { 274 if t, ok := converter(e); ok { 275 return t, true 276 } 277 } 278 return t, false 279 } 280 } 281 282 // FiltAndConv creates a loop that filters source elements and converts them 283 func FiltAndConv[From, To any](next func() (From, bool), filter func(From) (bool, error), converter func(From) (To, error)) breakloop.Loop[To] { 284 if next == nil { 285 return nil 286 } 287 return func() (t To, ok bool, err error) { 288 for { 289 if f, ok, err := Firstt(next, filter); err != nil || !ok { 290 return t, false, err 291 } else if cf, err := converter(f); err != nil { 292 return t, false, err 293 } else { 294 return cf, true, nil 295 } 296 } 297 } 298 } 299 300 // FilterAndConvert creates a loop that filters source elements and converts them 301 func FilterAndConvert[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To) Loop[To] { 302 return FilterConvertFilter(next, filter, converter, always.True[To]) 303 } 304 305 // FilterConvertFilter filters source, converts, and filters converted elements 306 func FilterConvertFilter[From, To any](next func() (From, bool), filter func(From) bool, converter func(From) To, filterTo func(To) bool) Loop[To] { 307 if next == nil { 308 return nil 309 } 310 return func() (t To, ok bool) { 311 for { 312 if f, ok := First(next, filter); !ok { 313 return t, false 314 } else if t = converter(f); filterTo(t) { 315 return t, ok 316 } 317 } 318 } 319 } 320 321 // ConvertAndFilter additionally filters 'To' elements 322 func ConvertAndFilter[From, To any](next func() (From, bool), converter func(From) To, filter func(To) bool) Loop[To] { 323 return FilterConvertFilter(next, always.True[From], converter, filter) 324 } 325 326 // Flatt creates a loop that extracts slices of 'To' by the 'flattener' function from iterable elements of 'From' and flattens as one iterable collection of 'To' elements. 327 func Flatt[From, To any](next func() (From, bool), flattener func(From) ([]To, error)) breakloop.Loop[To] { 328 return breakloop.Flatt(breakloop.From(next), flattener) 329 } 330 331 // FlattS 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. 332 func FlattS[FS ~[]From, From, To any](elements FS, flattener func(From) ([]To, error)) breakloop.Loop[To] { 333 return Flatt(S(elements), flattener) 334 } 335 336 // Flat creates a loop that extracts slices of 'To' by the 'flattener' function from iterable elements of 'From' and flattens as one iterable collection of 'To' elements. 337 func Flat[From, To any](next func() (From, bool), flattener func(From) []To) Loop[To] { 338 if next == nil { 339 return nil 340 } 341 var ( 342 elemSizeTo uintptr = notsafe.GetTypeSize[To]() 343 arrayTo unsafe.Pointer 344 indexTo, sizeTo int 345 ) 346 return func() (t To, ok bool) { 347 if sizeTo > 0 { 348 if indexTo < sizeTo { 349 i := indexTo 350 indexTo++ 351 return *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)), true 352 } 353 indexTo = 0 354 arrayTo = nil 355 sizeTo = 0 356 } 357 for { 358 if v, ok := next(); !ok { 359 var no To 360 return no, false 361 } else if elementsTo := flattener(v); len(elementsTo) > 0 { 362 indexTo = 1 363 header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) 364 arrayTo = unsafe.Pointer(header.Data) 365 sizeTo = header.Len 366 return *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)), true 367 } 368 } 369 } 370 } 371 372 // 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. 373 func FlatS[FS ~[]From, From, To any](elements FS, flattener func(From) []To) Loop[To] { 374 return Flat(S(elements), flattener) 375 } 376 377 // FiltAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function 378 func FiltAndFlat[From, To any](next func() (From, bool), filter func(From) (bool, error), flattener func(From) ([]To, error)) breakloop.Loop[To] { 379 return breakloop.FiltFlattFilt(breakloop.From(next), filter, flattener, breakAlways.True[To]) 380 } 381 382 // FilterAndFlat filters source elements and extracts slices of 'To' by the 'flattener' function 383 func FilterAndFlat[From, To any](next func() (From, bool), filter func(From) bool, flattener func(From) []To) Loop[To] { 384 return FilterFlatFilter(next, filter, flattener, always.True[To]) 385 } 386 387 // FlatAndFilt extracts slices of 'To' by the 'flattener' function and filters extracted elements 388 func FlatAndFilt[From, To any](next func() (From, bool, error), flattener func(From) ([]To, error), filterTo func(To) (bool, error)) breakloop.Loop[To] { 389 return breakloop.FiltFlattFilt(next, breakAlways.True[From], flattener, filterTo) 390 } 391 392 // FlattAndFilter extracts slices of 'To' by the 'flattener' function and filters extracted elements 393 func FlattAndFilter[From, To any](next func() (From, bool), flattener func(From) []To, filterTo func(To) bool) Loop[To] { 394 return FilterFlatFilter(next, always.True[From], flattener, filterTo) 395 } 396 397 // FiltFlattFilt filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements 398 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] { 399 return breakloop.FiltFlattFilt(breakloop.From(next), filterFrom, flattener, filterTo) 400 } 401 402 // FilterFlatFilter filters source elements, extracts slices of 'To' by the 'flattener' function and filters extracted elements 403 func FilterFlatFilter[From, To any](next func() (From, bool), filterFrom func(From) bool, flattener func(From) []To, filterTo func(To) bool) Loop[To] { 404 if next == nil { 405 return nil 406 } 407 var ( 408 elemSizeTo uintptr = notsafe.GetTypeSize[To]() 409 arrayTo unsafe.Pointer 410 indexTo, sizeTo int 411 ) 412 return func() (t To, ok bool) { 413 for { 414 if sizeTo > 0 { 415 if indexTo < sizeTo { 416 i := indexTo 417 indexTo++ 418 t = *(*To)(notsafe.GetArrayElemRef(arrayTo, i, elemSizeTo)) 419 if ok := filterTo(t); ok { 420 return t, true 421 } 422 } 423 indexTo = 0 424 arrayTo = nil 425 sizeTo = 0 426 } 427 428 if v, ok := next(); !ok { 429 return t, false 430 } else if filterFrom(v) { 431 if elementsTo := flattener(v); len(elementsTo) > 0 { 432 indexTo = 1 433 header := notsafe.GetSliceHeaderByRef(unsafe.Pointer(&elementsTo)) 434 arrayTo = unsafe.Pointer(header.Data) 435 sizeTo = header.Len 436 t = *(*To)(notsafe.GetArrayElemRef(arrayTo, 0, elemSizeTo)) 437 if ok := filterTo(t); ok { 438 return t, true 439 } 440 } 441 } 442 } 443 } 444 } 445 446 // Filt creates a loop that checks elements by the 'filter' function and returns successful ones. 447 func Filt[T any](next func() (T, bool), filter func(T) (bool, error)) breakloop.Loop[T] { 448 return breakloop.Filt(breakloop.From(next), filter) 449 } 450 451 // FiltS creates a loop that checks slice elements by the 'filter' function and returns successful ones. 452 func FiltS[TS ~[]T, T any](elements TS, filter func(T) (bool, error)) breakloop.Loop[T] { 453 return Filt(S(elements), filter) 454 } 455 456 // Filter creates a loop that checks elements by the 'filter' function and returns successful ones. 457 func Filter[T any](next func() (T, bool), filter func(T) bool) Loop[T] { 458 if next == nil { 459 return nil 460 } 461 return func() (T, bool) { 462 return First(next, filter) 463 } 464 } 465 466 // FilterS creates a loop that checks slice elements by the 'filter' function and returns successful ones. 467 func FilterS[TS ~[]T, T any](elements TS, filter func(T) bool) Loop[T] { 468 return Filter(S(elements), filter) 469 } 470 471 // NotNil creates a loop that filters nullable elements 472 func NotNil[T any](next func() (*T, bool)) Loop[*T] { 473 return Filter(next, not.Nil[T]) 474 } 475 476 // PtrVal creates a loop that transform pointers to the values referenced by those pointers. 477 // Nil pointers are transformet to zero values. 478 func PtrVal[T any](next func() (*T, bool)) Loop[T] { 479 return Convert(next, convert.PtrVal[T]) 480 } 481 482 // NoNilPtrVal creates a loop that transform only not nil pointers to the values referenced referenced by those pointers. 483 // Nil pointers are ignored. 484 func NoNilPtrVal[T any](next func() (*T, bool)) Loop[T] { 485 return ConvertCheck(next, convert.NoNilPtrVal[T]) 486 } 487 488 // KeyValue transforms a loop to the key/value loop based on applying key, value extractors to the elements 489 func KeyValue[T any, K, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) kvloop.Loop[K, V] { 490 if next == nil { 491 return nil 492 } 493 return func() (key K, value V, ok bool) { 494 if elem, nextOk := next(); nextOk { 495 key = keyExtractor(elem) 496 value = valExtractor(elem) 497 ok = true 498 } 499 return key, value, ok 500 } 501 } 502 503 // KeyValuee transforms a loop to the key/value loop based on applying key, value extractors to the elements 504 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] { 505 return breakloop.KeyValuee(breakloop.From(next), keyExtractor, valExtractor) 506 } 507 508 // KeysValues transforms a loop to the key/value loop based on applying multiple keys, values extractor to the elements 509 func KeysValues[T, K, V any](next func() (T, bool), keysExtractor func(T) []K, valsExtractor func(T) []V) kvloop.Loop[K, V] { 510 if next == nil { 511 return nil 512 } 513 var ( 514 keys []K 515 values []V 516 ki, vi int 517 ) 518 return func() (key K, value V, ok bool) { 519 for !ok { 520 var ( 521 keysLen, valuesLen = len(keys), len(values) 522 lastKeyIndex, lastValIndex = keysLen - 1, valuesLen - 1 523 ) 524 if keysLen > 0 && ki >= 0 && ki <= lastKeyIndex { 525 key = keys[ki] 526 ok = true 527 } 528 if valuesLen > 0 && vi >= 0 && vi <= lastValIndex { 529 value = values[vi] 530 ok = true 531 } 532 if ok { 533 if ki < lastKeyIndex { 534 ki++ 535 } else if vi < lastValIndex { 536 ki = 0 537 vi++ 538 } else { 539 keys, values = nil, nil 540 } 541 } else if elem, nextOk := next(); nextOk { 542 keys = keysExtractor(elem) 543 values = valsExtractor(elem) 544 ki, vi = 0, 0 545 } else { 546 keys, values = nil, nil 547 break 548 } 549 } 550 return key, value, ok 551 } 552 } 553 554 // KeysValue transforms a loop to the key/value loop based on applying keys, value extractor to the elements 555 func KeysValue[T, K, V any](next func() (T, bool), keysExtractor func(T) []K, valExtractor func(T) V) kvloop.Loop[K, V] { 556 return KeysValues(next, keysExtractor, func(t T) []V { return convert.AsSlice(valExtractor(t)) }) 557 } 558 559 // KeysValuee transforms a loop to the key/value loop based on applying keys, value extractor to the elements 560 func KeysValuee[T, K, V any](next func() (T, bool), keysExtractor func(T) ([]K, error), valExtractor func(T) (V, error)) breakkvloop.Loop[K, V] { 561 return breakloop.KeysValuee(breakloop.From(next), keysExtractor, valExtractor) 562 } 563 564 // KeyValues transforms a loop to the key/value loop based on applying key, values extractor to the elements 565 func KeyValues[T, K, V any](next func() (T, bool), keyExtractor func(T) K, valsExtractor func(T) []V) kvloop.Loop[K, V] { 566 return KeysValues(next, func(t T) []K { return convert.AsSlice(keyExtractor(t)) }, valsExtractor) 567 } 568 569 // KeyValuess transforms a loop to the key/value loop based on applying key, values extractor to the elements 570 func KeyValuess[T, K, V any](next func() (T, bool), keyExtractor func(T) (K, error), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[K, V] { 571 return breakloop.KeyValuess(breakloop.From(next), keyExtractor, valsExtractor) 572 } 573 574 // ExtraVals transforms a loop to the key/value loop based on applying values extractor to the elements 575 func ExtraVals[T, V any](next func() (T, bool), valsExtractor func(T) []V) kvloop.Loop[T, V] { 576 return KeyValues(next, as.Is[T], valsExtractor) 577 } 578 579 // ExtraValss transforms a loop to the key/value loop based on applying values extractor to the elements 580 func ExtraValss[T, V any](next func() (T, bool), valsExtractor func(T) ([]V, error)) breakkvloop.Loop[T, V] { 581 return KeyValuess(next, as.ErrTail(as.Is[T]), valsExtractor) 582 } 583 584 // ExtraKeys transforms a loop to the key/value loop based on applying key extractor to the elements 585 func ExtraKeys[T, K any](next func() (T, bool), keysExtractor func(T) []K) kvloop.Loop[K, T] { 586 return KeysValue(next, keysExtractor, as.Is[T]) 587 } 588 589 // ExtraKeyss transforms a loop to the key/value loop based on applying key extractor to the elements 590 func ExtraKeyss[T, K any](next func() (T, bool), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { 591 return KeyValuess(next, keyExtractor, as.ErrTail(convert.AsSlice[T])) 592 } 593 594 // ExtraKey transforms a loop to the key/value loop based on applying key extractor to the elements 595 func ExtraKey[T, K any](next func() (T, bool), keysExtractor func(T) K) kvloop.Loop[K, T] { 596 return KeyValue(next, keysExtractor, as.Is[T]) 597 } 598 599 // ExtraKeyy transforms a loop to the key/value loop based on applying key extractor to the elements 600 func ExtraKeyy[T, K any](next func() (T, bool), keyExtractor func(T) (K, error)) breakkvloop.Loop[K, T] { 601 return breakloop.KeyValuee[T, K](breakloop.From(next), keyExtractor, as.ErrTail(as.Is[T])) 602 } 603 604 // ExtraValue transforms a loop to the key/value loop based on applying value extractor to the elements 605 func ExtraValue[T, V any](next func() (T, bool), valueExtractor func(T) V) kvloop.Loop[T, V] { 606 return KeyValue(next, as.Is[T], valueExtractor) 607 } 608 609 // ExtraValuee transforms a loop to the key/value loop based on applying value extractor to the elements 610 func ExtraValuee[T, V any](next func() (T, bool), valExtractor func(T) (V, error)) breakkvloop.Loop[T, V] { 611 return breakloop.KeyValuee[T, T, V](breakloop.From(next), as.ErrTail(as.Is[T]), valExtractor) 612 } 613 614 // Group converts elements retrieved by the 'next' function into a map, extracting a key for each element applying the converter 'keyExtractor'. 615 // The keyExtractor converts an element to a key. 616 // The valExtractor converts an element to an value. 617 func Group[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) map[K][]V { 618 return ToMapResolv(next, keyExtractor, valExtractor, resolv.Slice[K, V]) 619 } 620 621 // Groupp converts elements retrieved by the 'next' function into a map, extracting a key for each element applying the converter 'keyExtractor'. 622 // The keyExtractor converts an element to a key. 623 // The valExtractor converts an element to an value. 624 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) { 625 return breakloop.Groupp(breakloop.From(next), keyExtractor, valExtractor) 626 } 627 628 // GroupByMultiple converts elements retrieved by the 'next' function into a map, extracting multiple keys, values per each element applying the 'keysExtractor' and 'valsExtractor' functions. 629 // The keysExtractor retrieves one or more keys per element. 630 // The valsExtractor retrieves one or more values per element. 631 func GroupByMultiple[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valsExtractor func(T) []V) map[K][]V { 632 if next == nil { 633 return nil 634 } 635 groups := map[K][]V{} 636 for e, ok := next(); ok; e, ok = next() { 637 if keys, vals := keysExtractor(e), valsExtractor(e); len(keys) == 0 { 638 var key K 639 for _, v := range vals { 640 initGroup(key, v, groups) 641 } 642 } else { 643 for _, key := range keys { 644 if len(vals) == 0 { 645 var v V 646 initGroup(key, v, groups) 647 } else { 648 for _, v := range vals { 649 initGroup(key, v, groups) 650 } 651 } 652 } 653 } 654 } 655 return groups 656 } 657 658 // GroupByMultipleKeys converts elements retrieved by the 'next' function into a map, extracting multiple keys, one value per each element applying the 'keysExtractor' and 'valExtractor' functions. 659 // The keysExtractor retrieves one or more keys per element. 660 // The valExtractor converts an element to a value. 661 func GroupByMultipleKeys[T any, K comparable, V any](next func() (T, bool), keysExtractor func(T) []K, valExtractor func(T) V) map[K][]V { 662 if next == nil { 663 return nil 664 } 665 groups := map[K][]V{} 666 for e, ok := next(); ok; e, ok = next() { 667 if keys, v := keysExtractor(e), valExtractor(e); len(keys) == 0 { 668 var key K 669 initGroup(key, v, groups) 670 } else { 671 for _, key := range keys { 672 initGroup(key, v, groups) 673 } 674 } 675 } 676 return groups 677 } 678 679 // GroupByMultipleValues converts elements retrieved by the 'next' function into a map, extracting one key, multiple values per each element applying the 'keyExtractor' and 'valsExtractor' functions. 680 // The keyExtractor converts an element to a key. 681 // The valsExtractor retrieves one or more values per element. 682 func GroupByMultipleValues[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valsExtractor func(T) []V) map[K][]V { 683 if next == nil { 684 return nil 685 } 686 groups := map[K][]V{} 687 for e, ok := next(); ok; e, ok = next() { 688 if key, vals := keyExtractor(e), valsExtractor(e); len(vals) == 0 { 689 var v V 690 initGroup(key, v, groups) 691 } else { 692 for _, v := range vals { 693 initGroup(key, v, groups) 694 } 695 } 696 } 697 return groups 698 } 699 700 func initGroup[T any, K comparable, TS ~[]T](key K, e T, groups map[K]TS) { 701 groups[key] = append(groups[key], e) 702 } 703 704 // ToMap collects key\value elements to a map by iterating over the elements 705 func ToMap[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) K, valExtractor func(T) V) map[K]V { 706 return ToMapResolv(next, keyExtractor, valExtractor, resolv.First[K, V]) 707 } 708 709 // ToMapp collects key\value elements to a map by iterating over the elements 710 func ToMapp[T any, K comparable, V any](next func() (T, bool), keyExtractor func(T) (K, error), valExtractor func(T) (V, error)) (map[K]V, error) { 711 return breakloop.ToMapp(breakloop.From(next), keyExtractor, valExtractor) 712 } 713 714 // ToMapResolv collects key\value elements to a map by iterating over the elements with resolving of duplicated key values 715 func ToMapResolv[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 { 716 if next == nil { 717 return nil 718 } 719 m := map[K]VR{} 720 for e, ok := next(); ok; e, ok = next() { 721 k, v := keyExtractor(e), valExtractor(e) 722 exists, ok := m[k] 723 m[k] = resolver(ok, k, exists, v) 724 } 725 return m 726 } 727 728 // Sequence makes a sequence by applying the 'next' function to the previous step generated value. 729 func Sequence[T any](first T, next func(T) (T, bool)) Loop[T] { 730 if next == nil { 731 return nil 732 } 733 current := first 734 init := true 735 return func() (out T, ok bool) { 736 if init { 737 init = false 738 return current, true 739 } else { 740 next, ok := next(current) 741 current = next 742 return current, ok 743 } 744 } 745 } 746 747 // RangeClosed creates a loop that generates integers in the range defined by from and to inclusive 748 func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) Loop[T] { 749 amount := toInclusive - from 750 delta := T(1) 751 if amount < 0 { 752 amount = -amount 753 delta = -delta 754 } 755 amount++ 756 nextElement := from 757 i := T(0) 758 return func() (out T, ok bool) { 759 if ok = i < amount; ok { 760 out = nextElement 761 i++ 762 nextElement = nextElement + delta 763 } 764 return out, ok 765 } 766 } 767 768 // Range creates a loop that generates integers in the range defined by from and to exclusive 769 func Range[T constraints.Integer | rune](from T, toExclusive T) Loop[T] { 770 amount := toExclusive - from 771 delta := T(1) 772 if amount < 0 { 773 amount = -amount 774 delta = -delta 775 } 776 nextElement := from 777 i := T(0) 778 return func() (out T, ok bool) { 779 if ok = i < amount; ok { 780 out = nextElement 781 i++ 782 nextElement = nextElement + delta 783 } 784 return out, ok 785 } 786 } 787 788 // OfIndexed builds a loop by extracting elements from an indexed soruce. 789 // the len is length ot the source. 790 // the getAt retrieves an element by its index from the source. 791 func OfIndexed[T any](len int, next func(int) T) Loop[T] { 792 if next == nil { 793 return nil 794 } 795 i := 0 796 return func() (out T, ok bool) { 797 if ok = i < len; ok { 798 out = next(i) 799 i++ 800 } 801 return out, ok 802 } 803 } 804 805 // ConvertAndReduce converts each elements and merges them into one 806 func ConvertAndReduce[From, To any](next func() (From, bool), converter func(From) To, merger func(To, To) To) (out To) { 807 if next == nil { 808 return out 809 } 810 if v, ok := next(); ok { 811 out = converter(v) 812 } else { 813 return out 814 } 815 for v, ok := next(); ok; v, ok = next() { 816 out = merger(out, converter(v)) 817 } 818 return out 819 } 820 821 // ConvAndReduce converts each elements and merges them into one 822 func ConvAndReduce[From, To any](next func() (From, bool), converter func(From) (To, error), merger func(To, To) To) (out To, err error) { 823 if next == nil { 824 return out, nil 825 } 826 if v, ok := next(); ok { 827 out, err = converter(v) 828 if err != nil { 829 return out, err 830 } 831 } else { 832 return out, nil 833 } 834 for v, ok := next(); ok; v, ok = next() { 835 c, err := converter(v) 836 if err != nil { 837 return out, err 838 } 839 out = merger(out, c) 840 } 841 return out, nil 842 } 843 844 // Crank rertieves a next element from the 'next' function, returns the function, element, successfully flag. 845 func Crank[T any](next func() (T, bool)) (n Loop[T], t T, ok bool) { 846 if next != nil { 847 t, ok = next() 848 } 849 return next, t, ok 850 }