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  }