|
| 1 | +# zeroflucs generics |
| 2 | + |
| 3 | +[](https://godoc.org/github.com/zeroflucs-given/generics) |
| 4 | + |
| 5 | + |
| 6 | +[](https://opensource.org/licenses/MIT) |
| 7 | + |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +### Overview |
| 12 | +The `generics` package is a series of helpers for writing generic Go code that provides a series of functions that don't exist |
| 13 | +in the mainline package set. |
| 14 | + |
| 15 | +## About ZeroFlucs |
| 16 | +[ZeroFlucs](https://zeroflucs.io) is a B2B provider of pricing technology for Sportsbooks/wagering service providers globally. We |
| 17 | +use Open-Source software through our platform stack. This, along with other projects is made available through our _zeroflucs-given_ |
| 18 | +Github profile on MIT licensing. To learn more you can visit: |
| 19 | + |
| 20 | +- [The ZeroFlucs Website](https://zeroflucs.io) - For information about our products and services. |
| 21 | +- [The ZeroFlucs Team Blog](https://blog.zeroflucs.io/) - For more content and posts from the ZeroFlucs team. |
| 22 | +- [ZeroFlucs-Given](https://github.com/zeroflucs-given/) - For more OSS contributions. |
| 23 | + |
| 24 | +## Why Does this Exist? |
| 25 | + |
| 26 | + |
| 27 | +When writing Go code for Go 1.17 or below, we've all written more than our fair share of methods to check "does this slice contain a thing", or "give me the first item matching a predicate". This package contains a roll-up of helpers and methods, as well as generic collection types that enable a lot of this boilerplate code to be removed. |
| 28 | + |
| 29 | +Key attributes: |
| 30 | + |
| 31 | + - Context support for filters/mappers (Optional) |
| 32 | + - Does not mutate input during operation. |
| 33 | + |
| 34 | +All code is covered 100% by tests for expected behaviour. Where filters or mappers are used |
| 35 | +methods are provided with and without context support. |
| 36 | + |
| 37 | +# Slice Queries |
| 38 | + |
| 39 | +``` |
| 40 | +package generics_test |
| 41 | +
|
| 42 | +import ( |
| 43 | + "fmt" |
| 44 | +
|
| 45 | + "github.com/zeroflucs-given/generics/query" |
| 46 | +) |
| 47 | +
|
| 48 | +func main() { |
| 49 | + inputs := []int{ |
| 50 | + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, |
| 51 | + } |
| 52 | + query := query.Slice(inputs) |
| 53 | +
|
| 54 | + result := query. |
| 55 | + Skip(5). |
| 56 | + TakeUntil(func(index int, v int) bool { |
| 57 | + return v >= 15 |
| 58 | + }). |
| 59 | + Filter(func(index int, v int) bool { |
| 60 | + return v%2 == 0 // Evens only |
| 61 | + }). |
| 62 | + Reverse(). |
| 63 | + ToSlice() // Back to a clean slice type |
| 64 | +
|
| 65 | + fmt.Println(result) // [14, 12, 10, 8, 6] |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +__But what about Contexts?__ |
| 70 | + |
| 71 | +Don't worry, we've got you. Use `.WithContext(ctx)` on the query chain and the entire subtree is now context aware. Any materialization functions that emit an actual value will now return an extra `error` argument. Operations are lazy, and will skip over the remainder of any query chain once the first error has occured. |
| 72 | + |
| 73 | +``` |
| 74 | +package generics_test |
| 75 | +
|
| 76 | +import ( |
| 77 | + "context" |
| 78 | + "fmt" |
| 79 | +
|
| 80 | + "github.com/zeroflucs-given/generics/query" |
| 81 | +) |
| 82 | +
|
| 83 | +func main() { |
| 84 | + inputs := []int{ |
| 85 | + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, |
| 86 | + } |
| 87 | + query := query.Slice(inputs) |
| 88 | + ctx := context.Todo() |
| 89 | +
|
| 90 | + result, err := query. |
| 91 | + WithContext(ctx). |
| 92 | + Skip(5). |
| 93 | + TakeUntil(func(ctx context.Context, index int, v int) (bool, error) { |
| 94 | + return v >= 15, nil |
| 95 | + }). |
| 96 | + Filter(func(ctx context.Context, index int, v int) (bool, error) { |
| 97 | + return v%2 == 0, nil // Evens only |
| 98 | + }). |
| 99 | + Reverse(). |
| 100 | + ToSlice() // Back to a clean slice type |
| 101 | +
|
| 102 | + fmt.Println(result) // [14, 12, 10, 8, 6] |
| 103 | + fmt.Println(err) // nil |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +When dealing with slice operations, the following design rules apply: |
| 108 | + |
| 109 | +- Operations that return a single value will return the type default if no item matches (i.e. 0 for numeric types, empty string or nil for objects) |
| 110 | +- Operations that accept multiple filters combine these filters into a logical AND by default. |
| 111 | +- If no filters are applied, every item is assumed to pass. |
| 112 | +- Context aware operations only exist where it makes sense (i.e. Take first 5 doesn't need a context, whereas take with filtering does) |
| 113 | + |
| 114 | +## Operation Detail |
| 115 | +### All / AllContext |
| 116 | +Returns true if all items in the slice match the filter. Will return true for an empty |
| 117 | +slice as no items fail the predicate. |
| 118 | + |
| 119 | +### Any / AnyContext |
| 120 | +Returns true if any item in the slice passes. |
| 121 | + |
| 122 | +### Concatenate |
| 123 | +Joins N slices of items together in the given order. Allocates a new slice. |
| 124 | + |
| 125 | +### Contains |
| 126 | +Returns true if the slice contains the specified value T, false otherwise. Uses standard equality operator to compare types. |
| 127 | + |
| 128 | +### Count / CountContext |
| 129 | +Returns the count of items matching the input filters. |
| 130 | + |
| 131 | +### DefaultIfEmpty |
| 132 | +Given a slice, if the slice is empty or nil will create a slice of a single default item. |
| 133 | + |
| 134 | +### Filter / FilterContext |
| 135 | +Creates a filtered set of the values in the slice, using a filter function. |
| 136 | + |
| 137 | +### First / FirstWithContext |
| 138 | +Returns the first item of the slice that matches the filters. If no value matches, returns |
| 139 | +the type default. |
| 140 | + |
| 141 | +### Group / GroupWithContext |
| 142 | +Uses a mapper function to assign input values to buckets. |
| 143 | + |
| 144 | +### Last / LastWithContext |
| 145 | +Returns the last item of the slice that matches the filter. |
| 146 | + |
| 147 | +### Map / MapWithContext |
| 148 | +Runs the specified mapper over each element of the input slice, creating an output slice of |
| 149 | +a different type. |
| 150 | + |
| 151 | +### Reverse |
| 152 | +Creates a reverse-sorted version of the input slice. |
| 153 | + |
| 154 | +### Skip |
| 155 | +Skip the first N elements of the slice. |
| 156 | + |
| 157 | +### SkipUntil / SkipUntilWithContext |
| 158 | +Skip items from the front of the slice until the predicate returns true. |
| 159 | + |
| 160 | +### SkipWhile / SkipWhileWithContext |
| 161 | +Skips items from the front of the slice until the predicate returns false. |
| 162 | + |
| 163 | +### Take |
| 164 | +Take the next N elements of the slice. |
| 165 | + |
| 166 | +### TakeUntil / TakeUntil |
| 167 | +Take items from the slice until the filter function returns true. |
| 168 | + |
| 169 | +### TakeWhile / TakeWhileWithContext |
| 170 | +Take items from the slice until the filter function returns false. |
| 171 | + |
| 172 | +### ToMap / ToMapWithContext |
| 173 | +Converts a slice of values to Go map, using mappers for the key and values respectively. |
| 174 | + |
| 175 | + |
| 176 | +## Slice Aggregations |
| 177 | +--------------- |
| 178 | +### Min |
| 179 | +Returns the minimum value from the input slice. Returns 0 if no values. |
| 180 | + |
| 181 | +### Max |
| 182 | +Returns the maximum value from the input slice. Returns 0 if no values. |
| 183 | + |
| 184 | +### Sum |
| 185 | +Returns the total sum of the values. Note that when handling large values, you may overflow your input type. |
| 186 | + |
| 187 | +# Map Operations |
| 188 | +The following map operation helpers exist in the `generics` package: |
| 189 | + |
| 190 | +## KeyValuesToMap |
| 191 | +Assembles a map from a slice of key-value pairs. |
| 192 | + |
| 193 | +## Keys |
| 194 | +Returns a slice of the key members of the map. |
| 195 | + |
| 196 | +## ToKeyValues |
| 197 | +Converts a map into a slice of key-value pairs. As per Go handling of maps, the order of |
| 198 | +output here is not in a guaranteed order. |
| 199 | + |
| 200 | +## Values |
| 201 | +Return a slice of the value members of the map. |
| 202 | + |
| 203 | +# Error Checking |
| 204 | +## Must |
| 205 | +Returns the first value in a pair of (T, error) if there is no error. Otherwise will panic. |
| 206 | + |
| 207 | +# Filter Building |
| 208 | +The `filtering` sub-package contains various constructs used to filter values. |
| 209 | + |
| 210 | +### True / TrueWithContext |
| 211 | +A constant value filter that always returns true. Useful for testing. |
| 212 | + |
| 213 | +### False / FalseWithContext |
| 214 | +A constant value filter that always returns false. Useful for testing. |
| 215 | + |
| 216 | +### And(...filters) / AndWithContext |
| 217 | +Creates a composite AND filter for multiple conditions. An empty set of filters is a true. |
| 218 | + |
| 219 | +### Or(...filters) / OrWithContext |
| 220 | +Creates a composite OR filter for multiple conditions. An empty set of filters is a false. |
| 221 | + |
| 222 | +### Not(filter) / NotWithContext |
| 223 | +Creates a NOT/inverted version of the specified filter to allow for negative checking. |
| 224 | + |
| 225 | +### Wrap |
| 226 | +Takes a non-context aware filter, but makes it compatible with code that expects contexts. |
0 commit comments