diff --git a/go.mod b/go.mod index f01e8c0..d79d2b4 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,19 @@ module github.com/rstudio/python-distribution-parser -go 1.24 +go 1.24.0 require ( github.com/bradleyjkemp/cupaloy v2.3.0+incompatible github.com/google/go-cmp v0.7.0 - github.com/samber/lo v1.51.0 + github.com/samber/lo v1.52.0 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.41.0 - golang.org/x/text v0.28.0 + golang.org/x/crypto v0.43.0 + golang.org/x/text v0.30.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.35.0 // indirect + golang.org/x/sys v0.37.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d0fc40b..31fd47c 100644 --- a/go.sum +++ b/go.sum @@ -6,16 +6,16 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= -github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/vendor/github.com/samber/lo/.golangci.yml b/vendor/github.com/samber/lo/.golangci.yml new file mode 100644 index 0000000..2a71979 --- /dev/null +++ b/vendor/github.com/samber/lo/.golangci.yml @@ -0,0 +1,104 @@ +version: "2" +run: + concurrency: 4 + # also lint _test.go files + tests: true + timeout: 5m +linters: + enable: + - govet + - staticcheck + - unused + - errcheck + - gocritic + - gocyclo + - revive + - ineffassign + - unconvert + - goconst + # - depguard + - prealloc + # - dupl + - misspell + - bodyclose + - sqlclosecheck + - nilerr + - nestif + - forcetypeassert + - exhaustive + - funlen + # - wsl_v5 + - testifylint + - whitespace + - perfsprint + - nolintlint + - godot + - thelper + - tparallel + - paralleltest + - predeclared + + # disable noisy/controversial ones which you might enable later + disable: + - lll # line length — handled by gfmt/gofumpt + + settings: + dupl: + threshold: 20 # lower => stricter (tokens) + errcheck: + check-type-assertions: true + funlen: + lines: 120 + statements: 80 + goconst: + min-len: 2 + min-occurrences: 3 + gocyclo: + min-complexity: 15 # strict; lower => stricter + wsl_v5: + allow-first-in-block: true + allow-whole-block: false + branch-max-lines: 2 + testifylint: + disable: + - require-error + - float-compare + + exclusions: + generated: lax + paths: + - examples$ + rules: + - linters: + - revive + text: "^unused-parameter:" + - linters: + - revive + text: "^package-comments:" + - linters: + - errcheck + text: "Error return value of `.*\\.Body\\.Close` is not checked" + # linters disabled in tests + - linters: + - dupl + - goconst + - funlen + path: "_test\\.go$" + +issues: + max-issues-per-linter: 0 # 0 = unlimited (we want ALL issues) + max-same-issues: 100 + +formatters: + enable: + - gofmt + - gofumpt + settings: + gofumpt: + extra-rules: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/samber/lo/Makefile b/vendor/github.com/samber/lo/Makefile index f97ded8..5a6591d 100644 --- a/vendor/github.com/samber/lo/Makefile +++ b/vendor/github.com/samber/lo/Makefile @@ -3,20 +3,19 @@ build: go build -v ./... test: - go test -race -v ./... + go test -race ./... watch-test: - reflex -t 50ms -s -- sh -c 'gotest -race -v ./...' + reflex -t 50ms -s -- sh -c 'gotest -race ./...' bench: - go test -benchmem -count 3 -bench ./... + go test -v -run=^Benchmark -benchmem -count 3 -bench ./... watch-bench: - reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...' + reflex -t 50ms -s -- sh -c 'go test -v -run=^Benchmark -benchmem -count 3 -bench ./...' coverage: go test -v -coverprofile=cover.out -covermode=atomic ./... go tool cover -html=cover.out -o cover.html -# tools tools: go install github.com/cespare/reflex@latest go install github.com/rakyll/gotest@latest @@ -25,18 +24,27 @@ tools: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest go get -t -u golang.org/x/tools/cmd/cover go install github.com/sonatype-nexus-community/nancy@latest + go install golang.org/x/perf/cmd/benchstat@latest + go install github.com/cespare/prettybench@latest go mod tidy + # brew install hougesen/tap/mdsf + lint: golangci-lint run --timeout 60s --max-same-issues 50 ./... + # mdsf verify --debug --log-level warn docs/ lint-fix: golangci-lint run --timeout 60s --max-same-issues 50 --fix ./... + # mdsf format --debug --log-level warn docs/ -audit: tools +audit: go list -json -m all | nancy sleuth -outdated: tools +outdated: go list -u -m -json all | go-mod-outdated -update -direct -weight: tools +weight: goweight + +doc: + cd docs && npm install && npm start diff --git a/vendor/github.com/samber/lo/README.md b/vendor/github.com/samber/lo/README.md index 87d2550..7a4ee33 100644 --- a/vendor/github.com/samber/lo/README.md +++ b/vendor/github.com/samber/lo/README.md @@ -21,6 +21,18 @@ A utility library based on Go 1.18+ generics that makes it easier to work with s - [samber/do](https://github.com/samber/do): A dependency injection toolkit based on Go 1.18+ Generics - [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...) +---- + +

šŸ’– Support This Project

+ +

+ I’m going all-in on open-source for the coming months. +
+ Help sustain development: Become an individual sponsor or join as a corporate sponsor. +

+ +---- + **Why this name?** I wanted a **short name**, similar to "Lodash", and no Go package uses this name. @@ -48,6 +60,7 @@ import ( "github.com/samber/lo" lop "github.com/samber/lo/parallel" lom "github.com/samber/lo/mutable" + loi "github.com/samber/lo/it" ) ``` @@ -68,11 +81,13 @@ import ( ) ``` -I take no responsibility on this junk. 😁 šŸ’© +I take no responsibility for this junk. 😁 šŸ’© ## 🤠 Spec -GoDoc: [https://godoc.org/github.com/samber/lo](https://godoc.org/github.com/samber/lo) +GoDoc: [godoc.org/github.com/samber/lo](https://godoc.org/github.com/samber/lo) + +Documentation: [lo.samber.dev](https://lo.samber.dev/docs/about) Supported helpers for slices: @@ -123,6 +138,14 @@ Supported helpers for slices: - [IsSorted](#issorted) - [IsSortedByKey](#issortedbykey) - [Splice](#Splice) +- [Cut](#Cut) +- [CutPrefix](#CutPrefix) +- [CutSuffix](#CutSuffix) +- [Trim](#Trim) +- [TrimLeft](#TrimLeft) +- [TrimPrefix](#TrimPrefix) +- [TrimRight](#TrimRight) +- [TrimSuffix](#TrimSuffix) Supported helpers for maps: @@ -142,11 +165,14 @@ Supported helpers for maps: - [FromEntries / FromPairs](#fromentries-alias-frompairs) - [Invert](#invert) - [Assign (merge of maps)](#assign) +- [ChunkEntries](#chunkentries) - [MapKeys](#mapkeys) - [MapValues](#mapvalues) - [MapEntries](#mapentries) - [MapToSlice](#maptoslice) - [FilterMapToSlice](#FilterMapToSlice) +- [FilterKeys](#FilterKeys) +- [FilterValues](#FilterValues) Supported math helpers: @@ -158,6 +184,7 @@ Supported math helpers: - [ProductBy](#productby) - [Mean](#mean) - [MeanBy](#meanby) +- [Mode](#mode) Supported helpers for strings: @@ -193,6 +220,7 @@ Supported helpers for channels: - [ChannelDispatcher](#channeldispatcher) - [SliceToChannel](#slicetochannel) +- [ChannelToSlice](#channeltoslice) - [Generator](#generator) - [Buffer](#buffer) - [BufferWithContext](#bufferwithcontext) @@ -224,6 +252,8 @@ Supported search helpers: - [IndexOf](#indexof) - [LastIndexOf](#lastindexof) +- [HasPrefix](#hasprefix) +- [HasSuffix](#hassuffix) - [Find](#find) - [FindIndexOf](#findindexof) - [FindLastIndexOf](#findlastindexof) @@ -310,6 +340,7 @@ Concurrency helpers: - [ThrottleByWithCount](#throttle) - [Synchronize](#synchronize) - [Async](#async) +- [Async{0->6}](#async0-6) - [Transaction](#transaction) - [WaitFor](#waitfor) - [WaitForWithContext](#waitforwithcontext) @@ -335,7 +366,7 @@ Constraints: ### Filter -Iterates over a collection and returns an array of all the elements the predicate function returns `true` for. +Iterates over a collection and returns a slice of all the elements the predicate function returns `true` for. ```go even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool { @@ -389,6 +420,8 @@ lop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string { // []string{"1", "2", "3", "4"} ``` +[[play](https://go.dev/play/p/sCJaB3quRMC)] + Mutable: like `lo.Map()`, but the slice is updated in place. ```go @@ -401,6 +434,8 @@ lom.Map(list, func(x int) int { // []int{2, 4, 6, 8} ``` +[[play](https://go.dev/play/p/0jY3Z0B7O_5)] + ### UniqMap Manipulates a slice and transforms it to a slice of another type with unique values. @@ -418,9 +453,11 @@ names := lo.UniqMap(users, func(u User, index int) string { // []string{"Alex", "Bob", "Alice"} ``` +[[play](https://go.dev/play/p/fygzLBhvUdB)] + ### FilterMap -Returns a slice which obtained after both filtering and mapping using the given callback function. +Returns a slice obtained after both filtering and mapping using the given callback function. The callback function should return two values: the result of the mapping operation and whether the result element should be included or not. @@ -526,7 +563,7 @@ lo.ForEachWhile(list, func(x int64, _ int) bool { ### Times -Times invokes the iteratee n times, returning an array of the results of each invocation. The iteratee is invoked with index as argument. +Times invokes the iteratee n times, returning a slice of the results of each invocation. The iteratee is invoked with index as argument. ```go import "github.com/samber/lo" @@ -552,7 +589,7 @@ lop.Times(3, func(i int) string { ### Uniq -Returns a duplicate-free version of an array, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array. +Returns a duplicate-free version of a slice, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the slice. ```go uniqValues := lo.Uniq([]int{1, 2, 2, 1}) @@ -563,7 +600,7 @@ uniqValues := lo.Uniq([]int{1, 2, 2, 1}) ### UniqBy -Returns a duplicate-free version of an array, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed. +Returns a duplicate-free version of a slice, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is invoked for each element in the slice to generate the criterion by which uniqueness is computed. ```go uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int { @@ -613,9 +650,11 @@ groups := lo.GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int) { // map[int][]int{0: []int{0, 6}, 1: []int{2, 8}, 2: []int{4, 10}} ``` +[[play](https://go.dev/play/p/iMeruQ3_W80)] + ### Chunk -Returns an array of elements split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements. +Returns a slice of elements split into groups of length size. If the slice can't be split evenly, the final chunk will be the remaining elements. ```go lo.Chunk([]int{0, 1, 2, 3, 4, 5}, 2) @@ -631,11 +670,11 @@ lo.Chunk([]int{0}, 2) // [][]int{{0}} ``` -[[play](https://go.dev/play/p/EeKl0AuTehH)] +[[play](https://go.dev/play/p/kEMkFbdu85g)] ### PartitionBy -Returns an array of elements split into groups. The order of grouped values is determined by the order they occur in collection. The grouping is generated from the results of running each element of collection through iteratee. +Returns a slice of elements split into groups. The order of grouped values is determined by the order they occur in collection. The grouping is generated from the results of running each element of collection through iteratee. ```go import lo "github.com/samber/lo" @@ -671,7 +710,7 @@ partitions := lop.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) strin ### Flatten -Returns an array a single level deep. +Returns a slice a single level deep. ```go flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}}) @@ -696,7 +735,7 @@ interleaved := lo.Interleave([]int{1}, []int{2, 5, 8}, []int{3, 6}, []int{4, 7, ### Shuffle -Returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm. +Returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm. āš ļø This helper is **mutable**. @@ -714,7 +753,7 @@ list ### Reverse -Reverses array so that the first element becomes the last, the second element becomes the second to last, and so on. +Reverses a slice so that the first element becomes the last, the second element becomes the second to last, and so on. āš ļø This helper is **mutable**. @@ -732,7 +771,7 @@ list ### Fill -Fills elements of array with `initial` value. +Fills elements of a slice with `initial` value. ```go type foo struct { @@ -788,7 +827,7 @@ slice := lo.RepeatBy(5, func(i int) string { ### KeyBy -Transforms a slice or an array of structs to a map based on a pivot callback. +Transforms a slice or a slice of structs to a map based on a pivot callback. ```go m := lo.KeyBy([]string{"a", "aa", "aaa"}, func(str string) int { @@ -815,9 +854,9 @@ result := lo.KeyBy(characters, func(char Character) string { ### SliceToMap (alias: Associate) Returns a map containing key-value pairs provided by transform function applied to elements of the given slice. -If any of two pairs would have the same key the last one gets added to the map. +If any of two pairs have the same key the last one gets added to the map. -The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. +The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. ```go in := []*foo{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}} @@ -834,13 +873,12 @@ aMap := lo.SliceToMap(in, func (f *foo) (string, int) { Returns a map containing key-value pairs provided by transform function applied to elements of the given slice. -If any of two pairs would have the same key the last one gets added to the map. +If any of two pairs have the same key the last one gets added to the map. -The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. +The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map. - ```go list := []string{"a", "aa", "aaa"} @@ -850,6 +888,8 @@ result := lo.FilterSliceToMap(list, func(str string) (string, int, bool) { // map[string][int]{"aa":2 "aaa":3} ``` +[[play](https://go.dev/play/p/2z0rDz2ZSGU)] + ### Keyify Returns a map with each unique element of the slice as a key. @@ -859,9 +899,11 @@ set := lo.Keyify([]int{1, 1, 2, 3, 4}) // map[int]struct{}{1:{}, 2:{}, 3:{}, 4:{}} ``` +[[play](https://go.dev/play/p/RYhhM_csqIG)] + ### Drop -Drops n elements from the beginning of a slice or array. +Drops n elements from the beginning of a slice. ```go l := lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2) @@ -872,7 +914,7 @@ l := lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2) ### DropRight -Drops n elements from the end of a slice or array. +Drops n elements from the end of a slice. ```go l := lo.DropRight([]int{0, 1, 2, 3, 4, 5}, 2) @@ -883,7 +925,7 @@ l := lo.DropRight([]int{0, 1, 2, 3, 4, 5}, 2) ### DropWhile -Drop elements from the beginning of a slice or array while the predicate returns true. +Drop elements from the beginning of a slice while the predicate returns true. ```go l := lo.DropWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool { @@ -896,7 +938,7 @@ l := lo.DropWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool ### DropRightWhile -Drop elements from the end of a slice or array while the predicate returns true. +Drop elements from the end of a slice while the predicate returns true. ```go l := lo.DropRightWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool { @@ -909,7 +951,7 @@ l := lo.DropRightWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) ### DropByIndex -Drops elements from a slice or array by the index. A negative index will drop elements from the end of the slice. +Drops elements from a slice by the index. A negative index will drop elements from the end of the slice. ```go l := lo.DropByIndex([]int{0, 1, 2, 3, 4, 5}, 2, 4, -1) @@ -920,7 +962,7 @@ l := lo.DropByIndex([]int{0, 1, 2, 3, 4, 5}, 2, 4, -1) ### Reject -The opposite of Filter, this method returns the elements of collection that predicate does not return truthy for. +The opposite of Filter, this method returns the elements of collection that predicate does not return true for. ```go odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool { @@ -933,7 +975,7 @@ odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool { ### RejectMap -The opposite of FilterMap, this method returns a slice which obtained after both filtering and mapping using the given callback function. +The opposite of FilterMap, this method returns a slice obtained after both filtering and mapping using the given callback function. The callback function should return two values: @@ -949,7 +991,7 @@ items := lo.RejectMap([]int{1, 2, 3, 4}, func(x int, _ int) (int, bool) { ### FilterReject -Mixes Filter and Reject, this method returns two slices, one for the elements of collection that predicate returns truthy for and one for the elements that predicate does not return truthy for. +Mixes Filter and Reject, this method returns two slices, one for the elements of collection that predicate returns true for and one for the elements that predicate does not return true for. ```go kept, rejected := lo.FilterReject([]int{1, 2, 3, 4}, func(x int, _ int) bool { @@ -961,7 +1003,7 @@ kept, rejected := lo.FilterReject([]int{1, 2, 3, 4}, func(x int, _ int) bool { ### Count -Counts the number of elements in the collection that compare equal to value. +Counts the number of elements in the collection that equal value. ```go count := lo.Count([]int{1, 5, 1}, 1) @@ -1008,7 +1050,7 @@ lo.CountValues([]string{"foo", "bar", "bar"}) ### CountValuesBy -Counts the number of each element in the collection. It ss equivalent to chaining lo.Map and lo.CountValues. +Counts the number of each element in the collection. It is equivalent to chaining lo.Map and lo.CountValues. ```go isEven := func(v int) bool { @@ -1170,7 +1212,140 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2") // []string{"a", "b", "1", "2"} ``` -[[play](https://go.dev/play/p/wiG6XyBBu49)] +[[play](https://go.dev/play/p/G5_GhkeSUBA)] + +### Cut + +Slices collection around the first instance of separator, returning the part of collection before and after separator. The found result reports whether separator appears in collection. If separator does not appear in s, cut returns collection, empty slice of []T, false. + +```go +actualLeft, actualRight, result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "c", "d"}) +// actualLeft: []string{"a"} +// actualRight: []string{"e", "f", "g"} +// result: true + +result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"z"}) +// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"} +// actualRight: []string{} +// result: false + +result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"}) +// actualLeft: []string{} +// actualRight: []string{"c", "d", "e", "f", "g"} +// result: true +``` + +[[play](https://go.dev/play/p/GiL3qhpIP3f)] + +### CutPrefix + +Returns collection without the provided leading prefix []T and reports whether it found the prefix. If s doesn't start with prefix, CutPrefix returns collection, false. If prefix is the empty []T, CutPrefix returns collection, true. + +```go +actualRight, result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c"}) +// actualRight: []string{"d", "e", "f", "g"} +// result: true + +result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b"}) +// actualRight: []string{"a", "b", "c", "d", "e", "f", "g"} +// result: false + +result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{}) +// actualRight: []string{"a", "b", "c", "d", "e", "f", "g"} +// result: true +``` + +[[play](https://go.dev/play/p/7Plak4a1ICl)] + +### CutSuffix + +Returns collection without the provided ending suffix []T and reports whether it found the suffix. If it doesn't end with suffix, CutSuffix returns collection, false. If suffix is the empty []T, CutSuffix returns collection, true. + +```go +actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"f", "g"}) +// actualLeft: []string{"a", "b", "c", "d", "e"} +// result: true + +actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b"}) +// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"} +// result: false + +actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{}) +// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"} +// result: true +``` + +[[play](https://go.dev/play/p/7FKfBFvPTaT)] + +### Trim + +Removes all the leading and trailing cutset from the collection. + +```go +result := lo.Trim([]int{0, 1, 2, 0, 3, 0}, []int{1, 0}) +// []int{2, 0, 3} + +result := lo.Trim([]string{"hello", "world", " "}, []string{" ", ""}) +// []string{"hello", "world"} +``` + +[[play](https://go.dev/play/p/1an9mxLdRG5)] + +### TrimLeft + +Removes all the leading cutset from the collection. + +```go +result := lo.TrimLeft([]int{0, 1, 2, 0, 3, 0}, []int{1, 0}) +// []int{2, 0, 3, 0} + +result := lo.TrimLeft([]string{"hello", "world", " "}, []string{" ", ""}) +// []string{"hello", "world", " "} +``` + +[[play](https://go.dev/play/p/74aqfAYLmyi)] + +### TrimPrefix + +Removes all the leading prefix from the collection. + +```go +result := lo.TrimPrefix([]int{1, 2, 1, 2, 3, 1, 2, 4}, []int{1, 2}) +// []int{3, 1, 2, 4} + +result := lo.TrimPrefix([]string{"hello", "world", "hello", "test"}, []string{"hello"}) +// []string{"world", "hello", "test"} +``` + +[[play](https://go.dev/play/p/SHO6X-YegPg)] + +### TrimRight + +Removes all the trailing cutset from the collection. + +```go +result := lo.TrimRight([]int{0, 1, 2, 0, 3, 0}, []int{0, 3}) +// []int{0, 1, 2} + +result := lo.TrimRight([]string{"hello", "world", " "}, []string{" ", ""}) +// []string{"hello", "world", ""} +``` + +[[play](https://go.dev/play/p/MRpAfR6sf0g)] + +### TrimSuffix + +Removes all the trailing suffix from the collection. + +```go +result := lo.TrimSuffix([]int{1, 2, 3, 1, 2, 4, 2, 4, 2, 4}, []int{2, 4}) +// []int{1, 2, 3, 1} + +result := lo.TrimSuffix([]string{"hello", "world", "hello", "test"}, []string{"test"}) +// []string{"hello", "world", "hello"} +``` + +[[play](https://go.dev/play/p/IjEUrV0iofq)] ### Keys @@ -1193,7 +1368,7 @@ keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3}) ### UniqKeys -Creates an array of unique map keys. +Creates a slice of unique map keys. ```go keys := lo.UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) @@ -1221,7 +1396,7 @@ exists := lo.HasKey(map[string]int{"foo": 1, "bar": 2}, "baz") ### Values -Creates an array of the map values. +Creates a slice of the map values. Use the UniqValues variant to deduplicate common values. @@ -1240,7 +1415,7 @@ values := lo.Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2} ### UniqValues -Creates an array of unique map values. +Creates a slice of unique map values. ```go values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}) @@ -1341,7 +1516,7 @@ m := lo.OmitByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3}) ### Entries (alias: ToPairs) -Transforms a map into array of key/value pairs. +Transforms a map into a slice of key/value pairs. ```go entries := lo.Entries(map[string]int{"foo": 1, "bar": 2}) @@ -1357,11 +1532,11 @@ entries := lo.Entries(map[string]int{"foo": 1, "bar": 2}) // } ``` -[[play](https://go.dev/play/p/3Dhgx46gawJ)] +[[play](https://go.dev/play/p/_t4Xe34-Nl5)] ### FromEntries (alias: FromPairs) -Transforms an array of key/value pairs into a map. +Transforms a slice of key/value pairs into a map. ```go m := lo.FromEntries([]lo.Entry[string, int]{ @@ -1409,7 +1584,7 @@ mergedMaps := lo.Assign( ### ChunkEntries -Splits a map into an array of elements in groups of a length equal to its size. If the map cannot be split evenly, the final chunk will contain the remaining elements. +Splits a map into a slice of elements in groups of length equal to its size. If the map cannot be split evenly, the final chunk will contain the remaining elements. ```go maps := lo.ChunkEntries( @@ -1431,7 +1606,7 @@ maps := lo.ChunkEntries( ### MapKeys -Manipulates a map keys and transforms it to a map of another type. +Manipulates map keys and transforms it to a map of another type. ```go m2 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string { @@ -1444,7 +1619,7 @@ m2 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string ### MapValues -Manipulates a map values and transforms it to a map of another type. +Manipulates map values and transforms it to a map of another type. ```go m1 := map[int]int64{1: 1, 2: 2, 3: 3} @@ -1459,7 +1634,7 @@ m2 := lo.MapValues(m1, func(x int64, _ int) string { ### MapEntries -Manipulates a map entries and transforms it to a map of another type. +Manipulates map entries and transforms it to a map of another type. ```go in := map[string]int{"foo": 1, "bar": 2} @@ -1474,7 +1649,7 @@ out := lo.MapEntries(in, func(k string, v int) (int, string) { ### MapToSlice -Transforms a map into a slice based on specific iteratee. +Transforms a map into a slice based on specified iteratee. ```go m := map[int]int64{1: 4, 2: 5, 3: 6} @@ -1489,7 +1664,7 @@ s := lo.MapToSlice(m, func(k int, v int64) string { ### FilterMapToSlice -Transforms a map into a slice based on specific iteratee. The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice. +Transforms a map into a slice based on specified iteratee. The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice. If the boolean is false, the value is not added to the result slice. The order of the keys in the input map is not specified and the order of the keys in the output slice is not guaranteed. @@ -1502,9 +1677,39 @@ result := lo.FilterMapToSlice(kv, func(k int, v int64) (string, bool) { // []{"2_2", "4_4"} ``` +### FilterKeys + +Transforms a map into a slice based on predicate returns true for specific elements. It is a mix of `lo.Filter()` and `lo.Keys()`. + +```go +kv := map[int]string{1: "foo", 2: "bar", 3: "baz"} + +result := FilterKeys(kv, func(k int, v string) bool { + return v == "foo" +}) +// [1] +``` + +[[play](https://go.dev/play/p/OFlKXlPrBAe)] + +### FilterValues + +Transforms a map into a slice based on predicate returns true for specific elements. It is a mix of `lo.Filter()` and `lo.Values()`. + +```go +kv := map[int]string{1: "foo", 2: "bar", 3: "baz"} + +result := FilterValues(kv, func(k int, v string) bool { + return v == "foo" +}) +// ["foo"] +``` + +[[play](https://go.dev/play/p/YVD5r_h-LX-)] + ### Range / RangeFrom / RangeWithSteps -Creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. +Creates a slice of numbers (positive and/or negative) progressing from start up to, but not including end. ```go result := lo.Range(4) @@ -1645,6 +1850,30 @@ mean := lo.MeanBy([]float64{}, mapper) // 0 ``` +[[play](https://go.dev/play/p/j7TsVwBOZ7P)] + +### Mode + +Calculates the mode (most frequent value) of a collection of numbers. + +If multiple values have the same highest frequency, then multiple values are returned. + +If the collection is empty, the zero value of `T[]` is returned. + +```go +mode := lo.Mode([]int{2, 2, 3, 4}) +// [2] + +mode := lo.Mode([]float64{2, 2, 3, 3}) +// [2, 3] + +mode := lo.Mode([]float64{}) +// [] + +mode := lo.Mode([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}) +// [1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + ### RandomString Returns a random string of the specified length and made of the specified charset. @@ -1675,7 +1904,7 @@ sub := lo.Substring("hello", -2, math.MaxUint) ### ChunkString -Returns an array of strings split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements. +Returns a slice of strings split into groups of length size. If the string can't be split evenly, the final chunk will be the remaining characters. ```go lo.ChunkString("123456", 2) @@ -1716,7 +1945,7 @@ str := lo.PascalCase("hello_world") // HelloWorld ``` -[[play](https://go.dev/play/p/iZkdeLP9oiB)] +[[play](https://go.dev/play/p/Dy_V_6DUYhe)] ### CamelCase @@ -1727,7 +1956,7 @@ str := lo.CamelCase("hello_world") // helloWorld ``` -[[play](https://go.dev/play/p/dtyFB58MBRp)] +[[play](https://go.dev/play/p/Go6aKwUiq59)] ### KebabCase @@ -1738,7 +1967,7 @@ str := lo.KebabCase("helloWorld") // hello-world ``` -[[play](https://go.dev/play/p/2YTuPafwECA)] +[[play](https://go.dev/play/p/96gT_WZnTVP)] ### SnakeCase @@ -1749,18 +1978,18 @@ str := lo.SnakeCase("HelloWorld") // hello_world ``` -[[play](https://go.dev/play/p/QVKJG9nOnDg)] +[[play](https://go.dev/play/p/ziB0V89IeVH)] ### Words -Splits string into an array of its words. +Splits string into a slice of its words. ```go str := lo.Words("helloWorld") // []string{"hello", "world"} ``` -[[play](https://go.dev/play/p/2P4zhqqq61g)] +[[play](https://go.dev/play/p/-f3VIQqiaVw)] ### Capitalize @@ -1771,9 +2000,11 @@ str := lo.Capitalize("heLLO") // Hello ``` +[[play](https://go.dev/play/p/uLTZZQXqnsa)] + ### Ellipsis -Trims and truncates a string to a specified length **in bytes** and appends an ellipsis if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8), truncating by byte length may split a character in the middle, potentially resulting in garbled output. +Trims and truncates a string to a specified length in `bytes` and appends an ellipsis if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8), truncating by byte length may split a character in the middle, potentially resulting in garbled output. ```go str := lo.Ellipsis(" Lorem Ipsum ", 5) @@ -1786,6 +2017,8 @@ str := lo.Ellipsis("Lorem Ipsum", 3) // ... ``` +[[play](https://go.dev/play/p/qE93rgqe1TW)] + ### T2 -> T9 Creates a tuple from a list of values. @@ -1803,7 +2036,7 @@ tuple2 := lo.T2(example()) ### Unpack2 -> Unpack9 -Returns values contained in tuple. +Returns values contained in a tuple. ```go r1, r2 := lo.Unpack2(lo.Tuple2[string, int]{"a", 1}) @@ -1822,9 +2055,9 @@ a, b := tuple2.Unpack() ### Zip2 -> Zip9 -Zip creates a slice of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on. +Zip creates a slice of grouped elements, the first of which contains the first elements of the given slices, the second of which contains the second elements of the given slices, and so on. -When collections have different size, the Tuple attributes are filled with zero value. +When collections are different sizes, the Tuple attributes are filled with zero value. ```go tuples := lo.Zip2([]string{"a", "b"}, []int{1, 2}) @@ -1835,9 +2068,9 @@ tuples := lo.Zip2([]string{"a", "b"}, []int{1, 2}) ### ZipBy2 -> ZipBy9 -ZipBy creates a slice of transformed elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on. +ZipBy creates a slice of transformed elements, the first of which contains the first elements of the given slices, the second of which contains the second elements of the given slices, and so on. -When collections have different size, the Tuple attributes are filled with zero value. +When collections are different sizes, the Tuple attributes are filled with zero value. ```go items := lo.ZipBy2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) string { @@ -1848,7 +2081,7 @@ items := lo.ZipBy2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) string ### Unzip2 -> Unzip9 -Unzip accepts an array of grouped elements and creates an array regrouping the elements to their pre-zip configuration. +Unzip accepts a slice of grouped elements and creates a slice regrouping the elements to their pre-zip configuration. ```go a, b := lo.Unzip2([]Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}}) @@ -1860,7 +2093,7 @@ a, b := lo.Unzip2([]Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}}) ### UnzipBy2 -> UnzipBy9 -UnzipBy2 iterates over a collection and creates an array regrouping the elements to their pre-zip configuration. +UnzipBy2 iterates over a collection and creates a slice regrouping the elements to their pre-zip configuration. ```go a, b := lo.UnzipBy2([]string{"hello", "john", "doe"}, func(str string) (string, int) { @@ -1872,7 +2105,7 @@ a, b := lo.UnzipBy2([]string{"hello", "john", "doe"}, func(str string) (string, ### CrossJoin2 -> CrossJoin9 -Combines every items from one list with every items from others. It is the cartesian product of lists received as arguments. It returns an empty list if a list is empty. +Combines every item from one list with every item from others. It is the cartesian product of lists received as arguments. Returns an empty list if a list is empty. ```go result := lo.CrossJoin2([]string{"hello", "john", "doe"}, []int{1, 2}) @@ -1886,7 +2119,7 @@ result := lo.CrossJoin2([]string{"hello", "john", "doe"}, []int{1, 2}) ### CrossJoinBy2 -> CrossJoinBy9 -Combines every items from one list with every items from others. It is the cartesian product of lists received as arguments. The project function is used to create the output values. It returns an empty list if a list is empty. +Combines every item from one list with every item from others. It is the cartesian product of lists received as arguments. The project function is used to create the output values. Returns an empty list if a list is empty. ```go result := lo.CrossJoinBy2([]string{"hello", "john", "doe"}, []int{1, 2}, func(a A, b B) string { @@ -1911,6 +2144,8 @@ duration := lo.Duration(func() { // 3s ``` +[[play](https://go.dev/play/p/HQfbBbAXaFP)] + ### Duration0 -> Duration10 Returns the time taken to execute a function. @@ -1923,7 +2158,7 @@ duration := lo.Duration0(func() { err, duration := lo.Duration1(func() error { // very long job - return fmt.Errorf("an error") + return errors.New("an error") }) // an error // 3s @@ -1971,6 +2206,8 @@ for i := range children { } ``` +[[play](https://go.dev/play/p/UZGu2wVg3J2)] + Many distributions strategies are available: - [lo.DispatchingStrategyRoundRobin](./channel.go): Distributes messages in a rotating sequential manner. @@ -2020,7 +2257,7 @@ children := lo.ChannelDispatcher(ch, 5, 10, customStrategy) ### SliceToChannel -Returns a read-only channels of collection elements. Channel is closed after last element. Channel capacity can be customized. +Returns a read-only channel of collection elements. Channel is closed after last element. Channel capacity can be customized. ```go list := []int{1, 2, 3, 4, 5} @@ -2031,9 +2268,11 @@ for v := range lo.SliceToChannel(2, list) { // prints 1, then 2, then 3, then 4, then 5 ``` +[[play](https://go.dev/play/p/lIbSY3QmiEg)] + ### ChannelToSlice -Returns a slice built from channels items. Blocks until channel closes. +Returns a slice built from channel items. Blocks until channel closes. ```go list := []int{1, 2, 3, 4, 5} @@ -2186,7 +2425,7 @@ for i := range children { ### FanIn -Merge messages from multiple input channels into a single buffered channel. Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. +Merge messages from multiple input channels into a single buffered channel. Output messages have no priority. When all upstream channels reach EOF, downstream channel closes. ```go stream1 := make(chan int, 42) @@ -2199,7 +2438,7 @@ all := lo.FanIn(100, stream1, stream2, stream3) ### FanOut -Broadcasts all the upstream messages to multiple downstream channels. When upstream channel reach EOF, downstream channels close. If any downstream channels is full, broadcasting is paused. +Broadcasts all the upstream messages to multiple downstream channels. When upstream channel reaches EOF, downstream channels close. If any downstream channels is full, broadcasting is paused. ```go stream := make(chan int, 42) @@ -2217,6 +2456,8 @@ present := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5) // true ``` +[[play](https://go.dev/play/p/W1EvyqY6t9j)] + ### ContainsBy Returns true if the predicate function returns `true`. @@ -2230,7 +2471,7 @@ present := lo.ContainsBy([]int{0, 1, 2, 3, 4, 5}, func(x int) bool { ### Every -Returns true if all elements of a subset are contained into a collection or if the subset is empty. +Returns true if all elements of a subset are contained in a collection or if the subset is empty. ```go ok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}) @@ -2251,14 +2492,19 @@ b := EveryBy([]int{1, 2, 3, 4}, func(x int) bool { // true ``` +[[play](https://go.dev/play/p/dn1-vhHsq9x)] + ### Some -Returns true if at least 1 element of a subset is contained into a collection. +Returns true if at least 1 element of a subset is contained in a collection. If the subset is empty Some returns false. ```go ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 6}) // true +``` + +[[play](https://go.dev/play/p/Lj4ceFkeT9V)] ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6}) // false @@ -2278,7 +2524,7 @@ b := SomeBy([]int{1, 2, 3, 4}, func(x int) bool { ### None -Returns true if no element of a subset are contained into a collection or if the subset is empty. +Returns true if no element of a subset is contained in a collection or if the subset is empty. ```go b := None([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}) @@ -2287,6 +2533,8 @@ b := None([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6}) // true ``` +[[play](https://go.dev/play/p/fye7JsmxzPV)] + ### NoneBy Returns true if the predicate returns true for none of the elements in the collection or if the collection is empty. @@ -2298,6 +2546,8 @@ b := NoneBy([]int{1, 2, 3, 4}, func(x int) bool { // true ``` +[[play](https://go.dev/play/p/O64WZ32H58S)] + ### Intersect Returns the intersection between two collections. @@ -2317,8 +2567,8 @@ result3 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6}) Returns the difference between two collections. -- The first value is the collection of element absent of list2. -- The second value is the collection of element absent of list1. +- The first value is the collection of elements absent from list2. +- The second value is the collection of elements absent from list1. ```go left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6}) @@ -2328,6 +2578,8 @@ left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5}) // []int{}, []int{} ``` +[[play](https://go.dev/play/p/pKE-JgzqRpz)] + ### Union Returns all distinct elements from given collections. Result will not change the order of elements relatively. @@ -2339,7 +2591,7 @@ union := lo.Union([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10}) ### Without -Returns slice excluding all given values. +Returns a slice excluding all given values. ```go subset := lo.Without([]int{0, 2, 10}, 2) @@ -2353,8 +2605,7 @@ subset := lo.Without([]int{0, 2, 10}, 0, 1, 2, 3, 4, 5) Filters a slice by excluding elements whose extracted keys match any in the exclude list. -It returns a new slice containing only the elements whose keys are not in the exclude list. - +Returns a new slice containing only the elements whose keys are not in the exclude list. ```go type struct User { @@ -2384,7 +2635,7 @@ filteredUsers := lo.WithoutBy(users, getID, excludedIDs...) ### WithoutEmpty -Returns slice excluding zero values. +Returns a slice excluding zero values. ```go subset := lo.WithoutEmpty([]int{0, 2, 10}) @@ -2393,7 +2644,7 @@ subset := lo.WithoutEmpty([]int{0, 2, 10}) ### WithoutNth -Returns slice excluding nth value. +Returns a slice excluding the nth value. ```go subset := lo.WithoutNth([]int{-2, -1, 0, 1, 2}, 3, -42, 1) @@ -2404,7 +2655,7 @@ subset := lo.WithoutNth([]int{-2, -1, 0, 1, 2}, 3, -42, 1) Returns true if lists contain the same set of elements (including empty set). -If there are duplicate elements, the number of appearances of each of them in both lists should match. +If there are duplicate elements, the number of occurrences in each list should match. The order of elements is not checked. @@ -2417,7 +2668,7 @@ b := lo.ElementsMatch([]int{1, 1, 2}, []int{2, 1, 1}) Returns true if lists contain the same set of elements' keys (including empty set). -If there are duplicate keys, the number of appearances of each of them in both lists should match. +If there are duplicate keys, the number of occurrences in each list should match. The order of elements is not checked. @@ -2432,7 +2683,7 @@ b := lo.ElementsMatchBy( ### IndexOf -Returns the index at which the first occurrence of a value is found in an array or return -1 if the value cannot be found. +Returns the index at which the first occurrence of a value is found in a slice or -1 if the value cannot be found. ```go found := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 2) @@ -2442,9 +2693,11 @@ notFound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6) // -1 ``` +[[play](https://go.dev/play/p/Eo7W0lvKTky)] + ### LastIndexOf -Returns the index at which the last occurrence of a value is found in an array or return -1 if the value cannot be found. +Returns the index at which the last occurrence of a value is found in a slice or -1 if the value cannot be found. ```go found := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 2) @@ -2454,9 +2707,37 @@ notFound := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 6) // -1 ``` +### HasPrefix + +Returns true if the collection has the prefix. + +```go +ok := lo.HasPrefix([]int{1, 2, 3, 4}, []int{42}) +// false + +ok := lo.HasPrefix([]int{1, 2, 3, 4}, []int{1, 2}) +// true +``` + +[[play](https://go.dev/play/p/SrljzVDpMQM)] + +### HasSuffix + +Returns true if the collection has the suffix. + +```go +ok := lo.HasSuffix([]int{1, 2, 3, 4}, []int{42}) +// false + +ok := lo.HasSuffix([]int{1, 2, 3, 4}, []int{3, 4}) +// true +``` + +[[play](https://go.dev/play/p/bJeLetQNAON)] + ### Find -Search an element in a slice based on a predicate. It returns element and true if element was found. +Searches for an element in a slice based on a predicate. Returns element and true if element was found. ```go str, ok := lo.Find([]string{"a", "b", "c", "d"}, func(i string) bool { @@ -2470,9 +2751,11 @@ str, ok := lo.Find([]string{"foobar"}, func(i string) bool { // "", false ``` +[[play](https://go.dev/play/p/Eo7W0lvKTky)] + ### FindIndexOf -FindIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found. +FindIndexOf searches for an element in a slice based on a predicate and returns the index and true. Returns -1 and false if the element is not found. ```go str, index, ok := lo.FindIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool { @@ -2486,9 +2769,11 @@ str, index, ok := lo.FindIndexOf([]string{"foobar"}, func(i string) bool { // "", -1, false ``` +[[play](https://go.dev/play/p/XWSEM4Ic_t0)] + ### FindLastIndexOf -FindLastIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found. +FindLastIndexOf searches for the last element in a slice based on a predicate and returns the index and true. Returns -1 and false if the element is not found. ```go str, index, ok := lo.FindLastIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool { @@ -2502,9 +2787,11 @@ str, index, ok := lo.FindLastIndexOf([]string{"foobar"}, func(i string) bool { // "", -1, false ``` +[[play](https://go.dev/play/p/dPiMRtJ6cUx)] + ### FindOrElse -Search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise. +Searches for an element in a slice based on a predicate. Returns the element if found or a given fallback value otherwise. ```go str := lo.FindOrElse([]string{"a", "b", "c", "d"}, "x", func(i string) bool { @@ -2538,7 +2825,7 @@ result3, ok3 := lo.FindKey(map[string]test{"foo": test{"foo"}, "bar": test{"bar" ### FindKeyBy -Returns the key of the first element predicate returns truthy for. +Returns the key of the first element predicate returns true for. ```go result1, ok1 := lo.FindKeyBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool { @@ -2554,7 +2841,7 @@ result2, ok2 := lo.FindKeyBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func( ### FindUniques -Returns a slice with all the unique elements of the collection. The order of result values is determined by the order they occur in the array. +Returns a slice with all the elements that appear in the collection only once. The order of result values is determined by the order they occur in the slice. ```go uniqueValues := lo.FindUniques([]int{1, 2, 2, 1, 2, 3}) @@ -2563,7 +2850,7 @@ uniqueValues := lo.FindUniques([]int{1, 2, 2, 1, 2, 3}) ### FindUniquesBy -Returns a slice with all the unique elements of the collection. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed. +Returns a slice with all the elements that appear in the collection only once. The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is invoked for each element in the slice to generate the criterion by which uniqueness is computed. ```go uniqueValues := lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int { @@ -2574,7 +2861,7 @@ uniqueValues := lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int { ### FindDuplicates -Returns a slice with the first occurrence of each duplicated elements of the collection. The order of result values is determined by the order they occur in the array. +Returns a slice with the first occurrence of each duplicated element in the collection. The order of result values is determined by the order they occur in the slice. ```go duplicatedValues := lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3}) @@ -2583,7 +2870,7 @@ duplicatedValues := lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3}) ### FindDuplicatesBy -Returns a slice with the first occurrence of each duplicated elements of the collection. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed. +Returns a slice with the first occurrence of each duplicated element in the collection. The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is invoked for each element in the slice to generate the criterion by which uniqueness is computed. ```go duplicatedValues := lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int { @@ -2609,6 +2896,8 @@ min := lo.Min([]time.Duration{time.Second, time.Hour}) // 1s ``` +[[play](https://go.dev/play/p/r6e-Z8JozS8)] + ### MinIndex Search the minimum value of a collection and the index of the minimum value. @@ -2885,7 +3174,7 @@ nth, err := lo.Nth([]int{0, 1, 2, 3}, -2) ### NthOr Returns the element at index `nth` of the collection. If `nth` is negative, it returns the `nth` element from the end. If `nth` is out of slice bounds, it returns the provided fallback value -```go +```go nth := lo.NthOr([]int{10, 20, 30, 40, 50}, 2, -1) // 30 @@ -2928,7 +3217,7 @@ lo.Sample([]string{}) // "" ``` - +[[play](https://go.dev/play/p/FYA45LcpfM2)] ### SampleBy @@ -2966,8 +3255,7 @@ lo.SamplesBy([]string{"a", "b", "c"}, 3, r.Intn) ### Ternary -A 1 line if/else statement. - +A single line if/else statement. ```go result := lo.Ternary(true, "a", "b") @@ -2983,7 +3271,7 @@ Take care to avoid dereferencing potentially nil pointers in your A/B expression ### TernaryF -A 1 line if/else statement whose options are functions. +A single line if/else statement whose options are functions. ```go result := lo.TernaryF(true, func() string { return "a" }, func() string { return "b" }) @@ -3158,6 +3446,8 @@ ptr := lo.ToPtr("hello world") // *string{"hello world"} ``` +[[play](https://go.dev/play/p/P2sD0PMXw4F)] + ### Nil Returns a nil pointer of type. @@ -3214,7 +3504,7 @@ value := lo.FromPtrOr(nil, "empty") ### ToSlicePtr -Returns a slice of pointer copy of value. +Returns a slice of pointers to each value. ```go ptr := lo.ToSlicePtr([]string{"hello", "world"}) @@ -3264,7 +3554,7 @@ elements := lo.ToAnySlice([]int{1, 5, 1}) ### FromAnySlice -Returns an `any` slice with all elements mapped to a type. Returns false in case of type conversion failure. +Returns a slice with all elements mapped to a type. Returns false in case of type conversion failure. ```go elements, ok := lo.FromAnySlice([]any{"foobar", 42}) @@ -3446,6 +3736,8 @@ f(42) // 47 ``` +[[play](https://go.dev/play/p/Sy1gAQiQZ3v)] + ### Partial2 -> Partial5 Returns new function that, when called, has its first argument set to the provided value. @@ -3461,6 +3753,8 @@ f(42, -4) // 80 ``` +[[play](https://go.dev/play/p/-xiPjy4JChJ)] + ### Attempt Invokes a function N times until it returns valid output. Returns either the caught error or nil. @@ -3473,7 +3767,7 @@ iter, err := lo.Attempt(42, func(i int) error { return nil } - return fmt.Errorf("failed") + return errors.New("failed") }) // 6 // nil @@ -3483,14 +3777,14 @@ iter, err := lo.Attempt(2, func(i int) error { return nil } - return fmt.Errorf("failed") + return errors.New("failed") }) // 2 // error "failed" iter, err := lo.Attempt(0, func(i int) error { if i < 42 { - return fmt.Errorf("failed") + return errors.New("failed") } return nil @@ -3499,7 +3793,7 @@ iter, err := lo.Attempt(0, func(i int) error { // nil ``` -For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). +For more advanced retry strategies (delay, exponential backoff...), please take a look at [cenkalti/backoff](https://github.com/cenkalti/backoff). [[play](https://go.dev/play/p/3ggJZ2ZKcMj)] @@ -3515,14 +3809,14 @@ iter, duration, err := lo.AttemptWithDelay(5, 2*time.Second, func(i int, duratio return nil } - return fmt.Errorf("failed") + return errors.New("failed") }) // 3 // ~ 4 seconds // nil ``` -For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). +For more advanced retry strategies (delay, exponential backoff...), please take a look at [cenkalti/backoff](https://github.com/cenkalti/backoff). [[play](https://go.dev/play/p/tVs6CygC7m1)] @@ -3536,7 +3830,7 @@ When the first argument is less than `1`, the function runs until a successful r count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) { err := doMockedHTTPRequest(i) if err != nil { - if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke + if errors.Is(err, ErrBadRequest) { // let's assume ErrBadRequest is a critical error that needs to terminate the invoke return err, false // flag the second return value as false to terminate the invoke } @@ -3547,9 +3841,9 @@ count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) { }) ``` -For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). +For more advanced retry strategies (delay, exponential backoff...), please take a look at [cenkalti/backoff](https://github.com/cenkalti/backoff). -[[play](https://go.dev/play/p/M2wVq24PaZM)] +[[play](https://go.dev/play/p/1VS7HxlYMOG)] ### AttemptWhileWithDelay @@ -3561,7 +3855,7 @@ When the first argument is less than `1`, the function runs until a successful r count1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int, d time.Duration) (error, bool) { err := doMockedHTTPRequest(i) if err != nil { - if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke + if errors.Is(err, ErrBadRequest) { // let's assume ErrBadRequest is a critical error that needs to terminate the invoke return err, false // flag the second return value as false to terminate the invoke } @@ -3572,9 +3866,9 @@ count1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int, }) ``` -For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). +For more advanced retry strategies (delay, exponential backoff...), please take a look at [cenkalti/backoff](https://github.com/cenkalti/backoff). -[[play](https://go.dev/play/p/cfcmhvLO-nv)] +[[play](https://go.dev/play/p/mhufUjJfLEF)] ### Debounce @@ -3715,8 +4009,8 @@ ch := lo.Async(func() error { time.Sleep(10 * time.Second); return nil }) ### Async{0->6} Executes a function in a goroutine and returns the result in a channel. -For function with multiple return values, the results will be returned as a tuple inside the channel. -For function without return, struct{} will be returned in the channel. +For functions with multiple return values, the results will be returned as a tuple inside the channel. +For functions without return, struct{} will be returned in the channel. ```go ch := lo.Async0(func() { time.Sleep(10 * time.Second) }) @@ -3766,7 +4060,7 @@ transaction := NewTransaction(). fmt.Println("step 3") if true { - return state, fmt.Errorf("error") + return state, errors.New("error") } return state + 42, nil @@ -3819,6 +4113,8 @@ iterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, 5*time.Mi // false ``` +[[play](https://go.dev/play/p/t_wTDmubbK3)] + ### WaitForWithContext Runs periodically until a condition is validated or context is invalid. @@ -3861,6 +4157,8 @@ iterations, duration, ok := lo.WaitForWithContext(expiringCtx, alwaysFalse, 100* // false ``` +[[play](https://go.dev/play/p/t_wTDmubbK3)] + ### Validate Helper function that creates an error when a condition is not met. @@ -3879,7 +4177,7 @@ val := lo.Validate(len(slice) == 0, "Slice should be empty but contains %v", sli ### Must -Wraps a function call to panics if second argument is `error` or `false`, returns the value otherwise. +Wraps a function call and panics if second argument is `error` or `false`, returns the value otherwise. ```go val := lo.Must(time.Parse("2006-01-02", "2022-01-15")) @@ -3893,7 +4191,7 @@ val := lo.Must(time.Parse("2006-01-02", "bad-value")) ### Must{0->6} -Must\* has the same behavior as Must, but returns multiple values. +Must\* has the same behavior as Must but returns multiple values. ```go func example0() (error) @@ -3956,7 +4254,7 @@ ok := lo.Try(func() error { // true ok := lo.Try(func() error { - return fmt.Errorf("error") + return errors.New("error") }) // false ``` @@ -3996,7 +4294,7 @@ str, ok := lo.TryOr(func() error { // true str, ok := lo.TryOr(func() error { - return "hello", fmt.Errorf("error") + return "hello", errors.New("error") }, "world") // world // false @@ -4085,7 +4383,7 @@ if ok := errors.As(err, &rateLimitErr); ok { } ``` -1 line `lo` helper: +single line `lo` helper: ```go err := doSomething() @@ -4165,7 +4463,7 @@ ok github.com/samber/lo 6.657s - `lo.Map` is way faster (x7) than `go-funk`, a reflection-based Map implementation. - `lo.Map` has the same allocation profile as `for`. - `lo.Map` is 4% slower than `for`. -- `lop.Map` is slower than `lo.Map` because it implies more memory allocation and locks. `lop.Map` will be useful for long-running callbacks, such as i/o bound processing. +- `lop.Map` is slower than `lo.Map` because it implies more memory allocation and locks. `lop.Map` is useful for long-running callbacks, such as i/o bound processing. - `for` beats other implementations for memory and CPU. ## šŸ¤ Contributing @@ -4178,14 +4476,6 @@ Don't hesitate ;) Helper naming: helpers must be self-explanatory and respect standards (other languages, libraries...). Feel free to suggest many names in your contributions. -### With Docker - -```bash -docker-compose run --rm dev -``` - -### Without Docker - ```bash # Install some dev dependencies make tools diff --git a/vendor/github.com/samber/lo/channel.go b/vendor/github.com/samber/lo/channel.go index a1e2cdd..9a10852 100644 --- a/vendor/github.com/samber/lo/channel.go +++ b/vendor/github.com/samber/lo/channel.go @@ -5,15 +5,17 @@ import ( "sync" "time" - "github.com/samber/lo/internal/rand" + "github.com/samber/lo/internal/xrand" ) +// DispatchingStrategy is a function that distributes messages to channels. type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) int // ChannelDispatcher distributes messages from input channels into N child channels. // Close events are propagated to children. // Underlying channels can have a fixed buffer capacity or be unbuffered when cap is 0. -func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T { +// Play: https://go.dev/play/p/UZGu2wVg3J2 +func ChannelDispatcher[T any](stream <-chan T, count, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T { children := createChannels[T](count, channelBufferCap) roChildren := channelsToReadOnly(children) @@ -22,7 +24,7 @@ func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, // propagate channel closing to children defer closeChannels(children) - var i uint64 = 0 + var i uint64 for { msg, ok := <-stream @@ -40,7 +42,7 @@ func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, return roChildren } -func createChannels[T any](count int, channelBufferCap int) []chan T { +func createChannels[T any](count, channelBufferCap int) []chan T { children := make([]chan T, 0, count) for i := 0; i < count; i++ { @@ -72,6 +74,7 @@ func channelIsNotFull[T any](ch <-chan T) bool { // DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner. // If the channel capacity is exceeded, the next channel will be selected and so on. +// Play: https://go.dev/play/p/UZGu2wVg3J2 func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int { for { i := int(index % uint64(len(channels))) @@ -86,9 +89,10 @@ func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan // DispatchingStrategyRandom distributes messages in a random manner. // If the channel capacity is exceeded, another random channel will be selected and so on. +// Play: https://go.dev/play/p/GEyGn3TdGk4 func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int { for { - i := rand.IntN(len(channels)) + i := xrand.IntN(len(channels)) if channelIsNotFull(channels[i]) { return i } @@ -99,6 +103,7 @@ func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) // DispatchingStrategyWeightedRandom distributes messages in a weighted manner. // If the channel capacity is exceeded, another random channel will be selected and so on. +// Play: https://go.dev/play/p/v0eMh8NZG2L func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] { seq := []int{} @@ -110,7 +115,7 @@ func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy return func(msg T, index uint64, channels []<-chan T) int { for { - i := seq[rand.IntN(len(seq))] + i := seq[xrand.IntN(len(seq))] if channelIsNotFull(channels[i]) { return i } @@ -122,6 +127,7 @@ func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy // DispatchingStrategyFirst distributes messages in the first non-full channel. // If the capacity of the first channel is exceeded, the second channel will be selected and so on. +// Play: https://go.dev/play/p/OrJCvOmk42f func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int { for { for i := range channels { @@ -135,25 +141,28 @@ func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) i } // DispatchingStrategyLeast distributes messages in the emptiest channel. +// Play: https://go.dev/play/p/ypy0jrRcEe7 func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int { seq := Range(len(channels)) - return MinBy(seq, func(item int, min int) bool { - return len(channels[item]) < len(channels[min]) + return MinBy(seq, func(item, mIn int) bool { + return len(channels[item]) < len(channels[mIn]) }) } // DispatchingStrategyMost distributes messages in the fullest channel. // If the channel capacity is exceeded, the next channel will be selected and so on. +// Play: https://go.dev/play/p/erHHone7rF9 func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int { seq := Range(len(channels)) - return MaxBy(seq, func(item int, max int) bool { - return len(channels[item]) > len(channels[max]) && channelIsNotFull(channels[item]) + return MaxBy(seq, func(item, mAx int) bool { + return len(channels[item]) > len(channels[mAx]) && channelIsNotFull(channels[item]) }) } -// SliceToChannel returns a read-only channels of collection elements. +// SliceToChannel returns a read-only channel of collection elements. +// Play: https://go.dev/play/p/lIbSY3QmiEg func SliceToChannel[T any](bufferSize int, collection []T) <-chan T { ch := make(chan T, bufferSize) @@ -168,7 +177,8 @@ func SliceToChannel[T any](bufferSize int, collection []T) <-chan T { return ch } -// ChannelToSlice returns a slice built from channels items. Blocks until channel closes. +// ChannelToSlice returns a slice built from channel items. Blocks until channel closes. +// Play: https://go.dev/play/p/lIbSY3QmiEg func ChannelToSlice[T any](ch <-chan T) []T { collection := []T{} @@ -180,6 +190,9 @@ func ChannelToSlice[T any](ch <-chan T) []T { } // Generator implements the generator design pattern. +// Play: https://go.dev/play/p/lIbSY3QmiEg +// +// Deprecated: use "iter" package instead (Go >= 1.23). func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T { ch := make(chan T, bufferSize) @@ -196,7 +209,8 @@ func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T { } // Buffer creates a slice of n elements from a channel. Returns the slice and the slice length. -// @TODO: we should probably provide an helper that reuse the same buffer. +// @TODO: we should probably provide a helper that reuses the same buffer. +// Play: https://go.dev/play/p/gPQ-6xmcKQI func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) { buffer := make([]T, 0, size) index := 0 @@ -222,7 +236,8 @@ func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime t } // BufferWithContext creates a slice of n elements from a channel, with context. Returns the slice and the slice length. -// @TODO: we should probably provide an helper that reuse the same buffer. +// @TODO: we should probably provide a helper that reuses the same buffer. +// Play: https://go.dev/play/p/oRfOyJWK9YF func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) { buffer := make([]T, 0, size) now := time.Now() @@ -245,6 +260,7 @@ func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (colle } // BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length. +// Play: https://go.dev/play/p/sxyEM3koo4n func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -259,7 +275,8 @@ func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (coll } // FanIn collects messages from multiple input channels into a single buffered channel. -// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. +// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes. +// Play: https://go.dev/play/p/FH8Wq-T04Jb func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { out := make(chan T, channelBufferCap) var wg sync.WaitGroup @@ -284,7 +301,7 @@ func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { } // ChannelMerge collects messages from multiple input channels into a single buffered channel. -// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes. +// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes. // // Deprecated: Use [FanIn] instead. func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { @@ -292,9 +309,10 @@ func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T { } // FanOut broadcasts all the upstream messages to multiple downstream channels. -// When upstream channel reach EOF, downstream channels close. If any downstream +// When upstream channel reaches EOF, downstream channels close. If any downstream // channels is full, broadcasting is paused. -func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T { +// Play: https://go.dev/play/p/2LHxcjKX23L +func FanOut[T any](count, channelsBufferCap int, upstream <-chan T) []<-chan T { downstreams := createChannels[T](count, channelsBufferCap) go func() { diff --git a/vendor/github.com/samber/lo/concurrency.go b/vendor/github.com/samber/lo/concurrency.go index a2ebbce..35e1f74 100644 --- a/vendor/github.com/samber/lo/concurrency.go +++ b/vendor/github.com/samber/lo/concurrency.go @@ -17,9 +17,10 @@ func (s *synchronize) Do(cb func()) { } // Synchronize wraps the underlying callback in a mutex. It receives an optional mutex. -func Synchronize(opt ...sync.Locker) *synchronize { +// Play: https://go.dev/play/p/X3cqROSpQmu +func Synchronize(opt ...sync.Locker) *synchronize { //nolint:revive if len(opt) > 1 { - panic("unexpected arguments") + panic("lo.Synchronize: unexpected arguments") } else if len(opt) == 0 { opt = append(opt, &sync.Mutex{}) } @@ -30,6 +31,7 @@ func Synchronize(opt ...sync.Locker) *synchronize { } // Async executes a function in a goroutine and returns the result in a channel. +// Play: https://go.dev/play/p/uo35gosuTLw func Async[A any](f func() A) <-chan A { ch := make(chan A, 1) go func() { @@ -39,6 +41,7 @@ func Async[A any](f func() A) <-chan A { } // Async0 executes a function in a goroutine and returns a channel set once the function finishes. +// Play: https://go.dev/play/p/tNqf1cClG_o func Async0(f func()) <-chan struct{} { ch := make(chan struct{}, 1) go func() { @@ -49,11 +52,13 @@ func Async0(f func()) <-chan struct{} { } // Async1 is an alias to Async. +// Play: https://go.dev/play/p/uo35gosuTLw func Async1[A any](f func() A) <-chan A { return Async(f) } // Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel. +// Play: https://go.dev/play/p/7W7mKQi0AhA func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] { ch := make(chan Tuple2[A, B], 1) go func() { @@ -63,6 +68,7 @@ func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] { } // Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel. +// Play: https://go.dev/play/p/L1d6o6l6q0d func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] { ch := make(chan Tuple3[A, B, C], 1) go func() { @@ -72,6 +78,7 @@ func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] { } // Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel. +// Play: https://go.dev/play/p/1X7q6oL0TqF func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] { ch := make(chan Tuple4[A, B, C, D], 1) go func() { @@ -81,6 +88,7 @@ func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] { } // Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel. +// Play: https://go.dev/play/p/2W7q4oL1TqG func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C, D, E] { ch := make(chan Tuple5[A, B, C, D, E], 1) go func() { @@ -90,6 +98,7 @@ func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C, } // Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel. +// Play: https://go.dev/play/p/3X8q5pM2UrH func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, B, C, D, E, F] { ch := make(chan Tuple6[A, B, C, D, E, F], 1) go func() { @@ -99,7 +108,8 @@ func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, } // WaitFor runs periodically until a condition is validated. -func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) { +// Play: https://go.dev/play/p/t_wTDmubbK3 +func WaitFor(condition func(i int) bool, timeout, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) { conditionWithContext := func(_ context.Context, currentIteration int) bool { return condition(currentIteration) } @@ -107,7 +117,8 @@ func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay t } // WaitForWithContext runs periodically until a condition is validated or context is canceled. -func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) { +// Play: https://go.dev/play/p/t_wTDmubbK3 +func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) { start := time.Now() if ctx.Err() != nil { diff --git a/vendor/github.com/samber/lo/condition.go b/vendor/github.com/samber/lo/condition.go index eaeaa2b..1eb017e 100644 --- a/vendor/github.com/samber/lo/condition.go +++ b/vendor/github.com/samber/lo/condition.go @@ -1,9 +1,9 @@ package lo -// Ternary is a 1 line if/else statement. +// Ternary is a single line if/else statement. // Take care to avoid dereferencing potentially nil pointers in your A/B expressions, because they are both evaluated. See TernaryF to avoid this problem. // Play: https://go.dev/play/p/t-D7WBL44h2 -func Ternary[T any](condition bool, ifOutput T, elseOutput T) T { +func Ternary[T any](condition bool, ifOutput, elseOutput T) T { if condition { return ifOutput } @@ -11,9 +11,9 @@ func Ternary[T any](condition bool, ifOutput T, elseOutput T) T { return elseOutput } -// TernaryF is a 1 line if/else statement whose options are functions +// TernaryF is a single line if/else statement whose options are functions. // Play: https://go.dev/play/p/AO4VW20JoqM -func TernaryF[T any](condition bool, ifFunc func() T, elseFunc func() T) T { +func TernaryF[T any](condition bool, ifFunc, elseFunc func() T) T { if condition { return ifFunc() } @@ -26,9 +26,9 @@ type ifElse[T any] struct { done bool } -// If. +// If is a single line if/else statement. // Play: https://go.dev/play/p/WSw3ApMxhyW -func If[T any](condition bool, result T) *ifElse[T] { +func If[T any](condition bool, result T) *ifElse[T] { //nolint:revive if condition { return &ifElse[T]{result, true} } @@ -37,9 +37,9 @@ func If[T any](condition bool, result T) *ifElse[T] { return &ifElse[T]{t, false} } -// IfF. +// IfF is a single line if/else statement whose options are functions. // Play: https://go.dev/play/p/WSw3ApMxhyW -func IfF[T any](condition bool, resultF func() T) *ifElse[T] { +func IfF[T any](condition bool, resultF func() T) *ifElse[T] { //nolint:revive if condition { return &ifElse[T]{resultF(), true} } @@ -98,7 +98,7 @@ type switchCase[T comparable, R any] struct { // Switch is a pure functional switch/case/default statement. // Play: https://go.dev/play/p/TGbKUMAeRUd -func Switch[T comparable, R any](predicate T) *switchCase[T, R] { +func Switch[T comparable, R any](predicate T) *switchCase[T, R] { //nolint:revive var result R return &switchCase[T, R]{ diff --git a/vendor/github.com/samber/lo/errors.go b/vendor/github.com/samber/lo/errors.go index a234721..135230a 100644 --- a/vendor/github.com/samber/lo/errors.go +++ b/vendor/github.com/samber/lo/errors.go @@ -25,7 +25,7 @@ func messageFromMsgAndArgs(msgAndArgs ...any) string { return fmt.Sprintf("%+v", msgAndArgs[0]) } if len(msgAndArgs) > 1 { - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) //nolint:errcheck,forcetypeassert } return "" } @@ -51,9 +51,8 @@ func must(err any, messageArgs ...any) { message := messageFromMsgAndArgs(messageArgs...) if message != "" { panic(message + ": " + e.Error()) - } else { - panic(e.Error()) } + panic(e.Error()) default: panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error") @@ -62,7 +61,7 @@ func must(err any, messageArgs ...any) { // Must is a helper that wraps a call to a function returning a value and an error // and panics if err is error or false. -// Play: https://go.dev/play/p/TMoWrRp3DyC +// Play: https://go.dev/play/p/fOqtX5HudtN func Must[T any](val T, err any, messageArgs ...any) T { must(err, messageArgs...) return val @@ -74,7 +73,7 @@ func Must0(err any, messageArgs ...any) { must(err, messageArgs...) } -// Must1 is an alias to Must +// Must1 is an alias to Must. // Play: https://go.dev/play/p/TMoWrRp3DyC func Must1[T any](val T, err any, messageArgs ...any) T { return Must(val, err, messageArgs...) @@ -130,7 +129,7 @@ func Try(callback func() error) (ok bool) { ok = false } - return + return ok } // Try0 has the same behavior as Try, but callback returns no variable. @@ -328,7 +327,7 @@ func TryWithErrorValue(callback func() error) (errorValue any, ok bool) { errorValue = err } - return + return errorValue, ok } // TryCatch has the same behavior as Try, but calls the catch function in case of error. diff --git a/vendor/github.com/samber/lo/find.go b/vendor/github.com/samber/lo/find.go index f04dc08..534b4d7 100644 --- a/vendor/github.com/samber/lo/find.go +++ b/vendor/github.com/samber/lo/find.go @@ -5,11 +5,12 @@ import ( "time" "github.com/samber/lo/internal/constraints" - "github.com/samber/lo/internal/rand" + "github.com/samber/lo/internal/xrand" ) -// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1 +// IndexOf returns the index at which the first occurrence of a value is found in a slice or -1 // if the value cannot be found. +// Play: https://go.dev/play/p/Eo7W0lvKTky func IndexOf[T comparable](collection []T, element T) int { for i := range collection { if collection[i] == element { @@ -20,8 +21,9 @@ func IndexOf[T comparable](collection []T, element T) int { return -1 } -// LastIndexOf returns the index at which the last occurrence of a value is found in an array or return -1 +// LastIndexOf returns the index at which the last occurrence of a value is found in a slice or -1 // if the value cannot be found. +// Play: https://go.dev/play/p/Eo7W0lvKTky func LastIndexOf[T comparable](collection []T, element T) int { length := len(collection) @@ -34,7 +36,40 @@ func LastIndexOf[T comparable](collection []T, element T) int { return -1 } -// Find search an element in a slice based on a predicate. It returns element and true if element was found. +// HasPrefix returns true if the collection has the prefix. +// Play: https://go.dev/play/p/SrljzVDpMQM +func HasPrefix[T comparable](collection, prefix []T) bool { + if len(collection) < len(prefix) { + return false + } + + for i := range prefix { + if collection[i] != prefix[i] { + return false + } + } + + return true +} + +// HasSuffix returns true if the collection has the suffix. +// Play: https://go.dev/play/p/bJeLetQNAON +func HasSuffix[T comparable](collection, suffix []T) bool { + if len(collection) < len(suffix) { + return false + } + + for i := range suffix { + if collection[len(collection)-len(suffix)+i] != suffix[i] { + return false + } + } + + return true +} + +// Find searches for an element in a slice based on a predicate. Returns element and true if element was found. +// Play: https://go.dev/play/p/Eo7W0lvKTky func Find[T any](collection []T, predicate func(item T) bool) (T, bool) { for i := range collection { if predicate(collection[i]) { @@ -46,8 +81,9 @@ func Find[T any](collection []T, predicate func(item T) bool) (T, bool) { return result, false } -// FindIndexOf searches an element in a slice based on a predicate and returns the index and true. -// It returns -1 and false if the element is not found. +// FindIndexOf searches for an element in a slice based on a predicate and returns the index and true. +// Returns -1 and false if the element is not found. +// Play: https://go.dev/play/p/XWSEM4Ic_t0 func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) { for i := range collection { if predicate(collection[i]) { @@ -59,8 +95,9 @@ func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bo return result, -1, false } -// FindLastIndexOf searches last element in a slice based on a predicate and returns the index and true. -// It returns -1 and false if the element is not found. +// FindLastIndexOf searches for the last element in a slice based on a predicate and returns the index and true. +// Returns -1 and false if the element is not found. +// Play: https://go.dev/play/p/dPiMRtJ6cUx func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) { length := len(collection) @@ -74,7 +111,8 @@ func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int return result, -1, false } -// FindOrElse search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise. +// FindOrElse searches for an element in a slice based on a predicate. Returns the element if found or a given fallback value otherwise. +// Play: https://go.dev/play/p/Eo7W0lvKTky func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) T { for i := range collection { if predicate(collection[i]) { @@ -86,9 +124,10 @@ func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) } // FindKey returns the key of the first value matching. -func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) { - for k := range object { - if object[k] == value { +// Play: https://go.dev/play/p/Bg0w1VDPYXx +func FindKey[K, V comparable](object map[K]V, value V) (K, bool) { + for k, v := range object { + if v == value { return k, true } } @@ -96,10 +135,11 @@ func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) { return Empty[K](), false } -// FindKeyBy returns the key of the first element predicate returns truthy for. +// FindKeyBy returns the key of the first element predicate returns true for. +// Play: https://go.dev/play/p/9IbiPElcyo8 func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool) { - for k := range object { - if predicate(k, object[k]) { + for k, v := range object { + if predicate(k, v) { return k, true } } @@ -107,7 +147,7 @@ func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value return Empty[K](), false } -// FindUniques returns a slice with all the unique elements of the collection. +// FindUniques returns a slice with all the elements that appear in the collection only once. // The order of result values is determined by the order they occur in the collection. func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice { isDupl := make(map[T]bool, len(collection)) @@ -132,9 +172,9 @@ func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice { return result } -// FindUniquesBy returns a slice with all the unique elements of the collection. -// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is -// invoked for each element in array to generate the criterion by which uniqueness is computed. +// FindUniquesBy returns a slice with all the elements that appear in the collection only once. +// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is +// invoked for each element in the slice to generate the criterion by which uniqueness is computed. func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice { isDupl := make(map[U]bool, len(collection)) @@ -162,7 +202,7 @@ func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee f return result } -// FindDuplicates returns a slice with the first occurrence of each duplicated elements of the collection. +// FindDuplicates returns a slice with the first occurrence of each duplicated element in the collection. // The order of result values is determined by the order they occur in the collection. func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice { isDupl := make(map[T]bool, len(collection)) @@ -188,9 +228,9 @@ func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice { return result } -// FindDuplicatesBy returns a slice with the first occurrence of each duplicated elements of the collection. -// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is -// invoked for each element in array to generate the criterion by which uniqueness is computed. +// FindDuplicatesBy returns a slice with the first occurrence of each duplicated element in the collection. +// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is +// invoked for each element in the slice to generate the criterion by which uniqueness is computed. func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice { isDupl := make(map[U]bool, len(collection)) @@ -221,122 +261,123 @@ func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iterate // Min search the minimum value of a collection. // Returns zero value when the collection is empty. +// Play: https://go.dev/play/p/r6e-Z8JozS8 func Min[T constraints.Ordered](collection []T) T { - var min T + var mIn T if len(collection) == 0 { - return min + return mIn } - min = collection[0] + mIn = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if item < min { - min = item + if item < mIn { + mIn = item } } - return min + return mIn } // MinIndex search the minimum value of a collection and the index of the minimum value. // Returns (zero value, -1) when the collection is empty. func MinIndex[T constraints.Ordered](collection []T) (T, int) { var ( - min T + mIn T index int ) if len(collection) == 0 { - return min, -1 + return mIn, -1 } - min = collection[0] + mIn = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if item < min { - min = item + if item < mIn { + mIn = item index = i } } - return min, index + return mIn, index } // MinBy search the minimum value of a collection using the given comparison function. // If several values of the collection are equal to the smallest value, returns the first such value. // Returns zero value when the collection is empty. -func MinBy[T any](collection []T, comparison func(a T, b T) bool) T { - var min T +func MinBy[T any](collection []T, comparison func(a, b T) bool) T { + var mIn T if len(collection) == 0 { - return min + return mIn } - min = collection[0] + mIn = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if comparison(item, min) { - min = item + if comparison(item, mIn) { + mIn = item } } - return min + return mIn } // MinIndexBy search the minimum value of a collection using the given comparison function and the index of the minimum value. // If several values of the collection are equal to the smallest value, returns the first such value. // Returns (zero value, -1) when the collection is empty. -func MinIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) { +func MinIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) { var ( - min T + mIn T index int ) if len(collection) == 0 { - return min, -1 + return mIn, -1 } - min = collection[0] + mIn = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if comparison(item, min) { - min = item + if comparison(item, mIn) { + mIn = item index = i } } - return min, index + return mIn, index } // Earliest search the minimum time.Time of a collection. // Returns zero value when the collection is empty. func Earliest(times ...time.Time) time.Time { - var min time.Time + var mIn time.Time if len(times) == 0 { - return min + return mIn } - min = times[0] + mIn = times[0] for i := 1; i < len(times); i++ { item := times[i] - if item.Before(min) { - min = item + if item.Before(mIn) { + mIn = item } } - return min + return mIn } // EarliestBy search the minimum time.Time of a collection using the given iteratee function. @@ -365,122 +406,123 @@ func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T { // Max searches the maximum value of a collection. // Returns zero value when the collection is empty. +// Play: https://go.dev/play/p/r6e-Z8JozS8 func Max[T constraints.Ordered](collection []T) T { - var max T + var mAx T if len(collection) == 0 { - return max + return mAx } - max = collection[0] + mAx = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if item > max { - max = item + if item > mAx { + mAx = item } } - return max + return mAx } // MaxIndex searches the maximum value of a collection and the index of the maximum value. // Returns (zero value, -1) when the collection is empty. func MaxIndex[T constraints.Ordered](collection []T) (T, int) { var ( - max T + mAx T index int ) if len(collection) == 0 { - return max, -1 + return mAx, -1 } - max = collection[0] + mAx = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if item > max { - max = item + if item > mAx { + mAx = item index = i } } - return max, index + return mAx, index } // MaxBy search the maximum value of a collection using the given comparison function. // If several values of the collection are equal to the greatest value, returns the first such value. // Returns zero value when the collection is empty. -func MaxBy[T any](collection []T, comparison func(a T, b T) bool) T { - var max T +func MaxBy[T any](collection []T, comparison func(a, b T) bool) T { + var mAx T if len(collection) == 0 { - return max + return mAx } - max = collection[0] + mAx = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if comparison(item, max) { - max = item + if comparison(item, mAx) { + mAx = item } } - return max + return mAx } // MaxIndexBy search the maximum value of a collection using the given comparison function and the index of the maximum value. // If several values of the collection are equal to the greatest value, returns the first such value. // Returns (zero value, -1) when the collection is empty. -func MaxIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) { +func MaxIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) { var ( - max T + mAx T index int ) if len(collection) == 0 { - return max, -1 + return mAx, -1 } - max = collection[0] + mAx = collection[0] for i := 1; i < len(collection); i++ { item := collection[i] - if comparison(item, max) { - max = item + if comparison(item, mAx) { + mAx = item index = i } } - return max, index + return mAx, index } // Latest search the maximum time.Time of a collection. // Returns zero value when the collection is empty. func Latest(times ...time.Time) time.Time { - var max time.Time + var mAx time.Time if len(times) == 0 { - return max + return mAx } - max = times[0] + mAx = times[0] for i := 1; i < len(times); i++ { item := times[i] - if item.After(max) { - max = item + if item.After(mAx) { + mAx = item } } - return max + return mAx } // LatestBy search the maximum time.Time of a collection using the given iteratee function. @@ -508,6 +550,7 @@ func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T { } // First returns the first element of a collection and check for availability of the first element. +// Play: https://go.dev/play/p/ul45Z0y2EFO func First[T any](collection []T) (T, bool) { length := len(collection) @@ -520,12 +563,14 @@ func First[T any](collection []T) (T, bool) { } // FirstOrEmpty returns the first element of a collection or zero value if empty. +// Play: https://go.dev/play/p/ul45Z0y2EFO func FirstOrEmpty[T any](collection []T) T { i, _ := First(collection) return i } // FirstOr returns the first element of a collection or the fallback value if empty. +// Play: https://go.dev/play/p/ul45Z0y2EFO func FirstOr[T any](collection []T, fallback T) T { i, ok := First(collection) if !ok { @@ -536,6 +581,7 @@ func FirstOr[T any](collection []T, fallback T) T { } // Last returns the last element of a collection or error if empty. +// Play: https://go.dev/play/p/ul45Z0y2EFO func Last[T any](collection []T) (T, bool) { length := len(collection) @@ -548,12 +594,14 @@ func Last[T any](collection []T) (T, bool) { } // LastOrEmpty returns the last element of a collection or zero value if empty. +// Play: https://go.dev/play/p/ul45Z0y2EFO func LastOrEmpty[T any](collection []T) T { i, _ := Last(collection) return i } // LastOr returns the last element of a collection or the fallback value if empty. +// Play: https://go.dev/play/p/ul45Z0y2EFO func LastOr[T any](collection []T, fallback T) T { i, ok := Last(collection) if !ok { @@ -565,6 +613,7 @@ func LastOr[T any](collection []T, fallback T) T { // Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element // from the end is returned. An error is returned when nth is out of slice bounds. +// Play: https://go.dev/play/p/sHoh88KWt6B func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) { n := int(nth) l := len(collection) @@ -582,6 +631,7 @@ func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) { // NthOr returns the element at index `nth` of collection. // If `nth` is negative, it returns the nth element from the end. // If `nth` is out of slice bounds, it returns the fallback value instead of an error. +// Play: https://go.dev/play/p/sHoh88KWt6B func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T { value, err := Nth(collection, nth) if err != nil { @@ -593,6 +643,7 @@ func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T { // NthOrEmpty returns the element at index `nth` of collection. // If `nth` is negative, it returns the nth element from the end. // If `nth` is out of slice bounds, it returns the zero value (empty value) for that type. +// Play: https://go.dev/play/p/sHoh88KWt6B func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T { value, err := Nth(collection, nth) if err != nil { @@ -603,16 +654,18 @@ func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T { } // randomIntGenerator is a function that should return a random integer in the range [0, n) -// where n is the parameter passed to the randomIntGenerator. +// where n is the argument passed to the randomIntGenerator. type randomIntGenerator func(n int) int // Sample returns a random item from collection. +// Play: https://go.dev/play/p/vCcSJbh5s6l func Sample[T any](collection []T) T { - result := SampleBy(collection, rand.IntN) + result := SampleBy(collection, xrand.IntN) return result } // SampleBy returns a random item from collection, using randomIntGenerator as the random index generator. +// Play: https://go.dev/play/p/HDmKmMgq0XN func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T { size := len(collection) if size == 0 { @@ -622,16 +675,18 @@ func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T { } // Samples returns N random unique items from collection. +// Play: https://go.dev/play/p/vCcSJbh5s6l func Samples[T any, Slice ~[]T](collection Slice, count int) Slice { - results := SamplesBy(collection, count, rand.IntN) + results := SamplesBy(collection, count, xrand.IntN) return results } // SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator. +// Play: https://go.dev/play/p/HDmKmMgq0XN func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator randomIntGenerator) Slice { size := len(collection) - copy := append(Slice{}, collection...) + cOpy := append(Slice{}, collection...) results := Slice{} @@ -639,12 +694,12 @@ func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerato copyLength := size - i index := randomIntGenerator(size - i) - results = append(results, copy[index]) + results = append(results, cOpy[index]) // Removes element. // It is faster to swap with last element and remove it. - copy[index] = copy[copyLength-1] - copy = copy[:copyLength-1] + cOpy[index] = cOpy[copyLength-1] + cOpy = cOpy[:copyLength-1] } return results diff --git a/vendor/github.com/samber/lo/func.go b/vendor/github.com/samber/lo/func.go index 5fa1cbc..c1e9c93 100644 --- a/vendor/github.com/samber/lo/func.go +++ b/vendor/github.com/samber/lo/func.go @@ -1,6 +1,7 @@ package lo // Partial returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/Sy1gAQiQZ3v func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R { return func(t2 T2) R { return f(arg1, t2) @@ -8,11 +9,13 @@ func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R { } // Partial1 returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/D-ASTXCLBzw func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R { return Partial(f, arg1) } // Partial2 returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/-xiPjy4JChJ func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R { return func(t2 T2, t3 T3) R { return f(arg1, t2, t3) @@ -20,6 +23,7 @@ func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R { } // Partial3 returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/zWtSutpI26m func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, T3, T4) R { return func(t2 T2, t3 T3, t4 T4) R { return f(arg1, t2, t3, t4) @@ -27,13 +31,15 @@ func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, } // Partial4 returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/kBrnnMTcJm0 func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1) func(T2, T3, T4, T5) R { return func(t2 T2, t3 T3, t4 T4, t5 T5) R { return f(arg1, t2, t3, t4, t5) } } -// Partial5 returns new function that, when called, has its first argument set to the provided value +// Partial5 returns new function that, when called, has its first argument set to the provided value. +// Play: https://go.dev/play/p/7Is7K2y_VC3 func Partial5[T1, T2, T3, T4, T5, T6, R any](f func(T1, T2, T3, T4, T5, T6) R, arg1 T1) func(T2, T3, T4, T5, T6) R { return func(t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R { return f(arg1, t2, t3, t4, t5, t6) diff --git a/vendor/github.com/samber/lo/internal/constraints/README.md b/vendor/github.com/samber/lo/internal/constraints/README.md new file mode 100644 index 0000000..27a3b1c --- /dev/null +++ b/vendor/github.com/samber/lo/internal/constraints/README.md @@ -0,0 +1,4 @@ + +# Constraints + +This package is for Go 1.18 retrocompatiblity purpose. diff --git a/vendor/github.com/samber/lo/internal/constraints/ordered_go121.go b/vendor/github.com/samber/lo/internal/constraints/ordered_go121.go index c02de93..085a743 100644 --- a/vendor/github.com/samber/lo/internal/constraints/ordered_go121.go +++ b/vendor/github.com/samber/lo/internal/constraints/ordered_go121.go @@ -6,4 +6,8 @@ import ( "cmp" ) +// Ordered is a constraint that permits any ordered type: any type +// that supports the operators < <= >= >. +// If future releases of Go add new ordered types, +// this constraint will be modified to include them. type Ordered = cmp.Ordered diff --git a/vendor/github.com/samber/lo/internal/rand/ordered_go122.go b/vendor/github.com/samber/lo/internal/rand/ordered_go122.go deleted file mode 100644 index 65abf51..0000000 --- a/vendor/github.com/samber/lo/internal/rand/ordered_go122.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build go1.22 - -package rand - -import "math/rand/v2" - -func Shuffle(n int, swap func(i, j int)) { - rand.Shuffle(n, swap) -} - -func IntN(n int) int { - return rand.IntN(n) -} - -func Int64() int64 { - return rand.Int64() -} diff --git a/vendor/github.com/samber/lo/internal/rand/ordered_go118.go b/vendor/github.com/samber/lo/internal/xrand/ordered_go118.go similarity index 53% rename from vendor/github.com/samber/lo/internal/rand/ordered_go118.go rename to vendor/github.com/samber/lo/internal/xrand/ordered_go118.go index 9fbc538..410a5f3 100644 --- a/vendor/github.com/samber/lo/internal/rand/ordered_go118.go +++ b/vendor/github.com/samber/lo/internal/xrand/ordered_go118.go @@ -1,22 +1,28 @@ //go:build !go1.22 -package rand +package xrand import "math/rand" +// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm. func Shuffle(n int, swap func(i, j int)) { rand.Shuffle(n, swap) } +// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. func IntN(n int) int { // bearer:disable go_gosec_crypto_weak_random return rand.Intn(n) } +// Int64 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. func Int64() int64 { // bearer:disable go_gosec_crypto_weak_random n := rand.Int63() - + // bearer:disable go_gosec_crypto_weak_random if rand.Intn(2) == 0 { return -n diff --git a/vendor/github.com/samber/lo/internal/xrand/ordered_go122.go b/vendor/github.com/samber/lo/internal/xrand/ordered_go122.go new file mode 100644 index 0000000..6bca250 --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xrand/ordered_go122.go @@ -0,0 +1,23 @@ +//go:build go1.22 + +package xrand + +import "math/rand/v2" + +// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm. +func Shuffle(n int, swap func(i, j int)) { + rand.Shuffle(n, swap) +} + +// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func IntN(n int) int { + return rand.IntN(n) +} + +// Int64 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. +func Int64() int64 { + return rand.Int64() +} diff --git a/vendor/github.com/samber/lo/internal/xtime/README.md b/vendor/github.com/samber/lo/internal/xtime/README.md new file mode 100644 index 0000000..73acd1e --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xtime/README.md @@ -0,0 +1,6 @@ + +# xtime + +Lightweight mock for time package. + +A dedicated package such as [jonboulle/clockwork](https://github.com/jonboulle/clockwork/) would be better, but I would rather limit dependencies for this package. `clockwork` does not support Go 1.18 anymore. diff --git a/vendor/github.com/samber/lo/internal/xtime/fake.go b/vendor/github.com/samber/lo/internal/xtime/fake.go new file mode 100644 index 0000000..380e238 --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xtime/fake.go @@ -0,0 +1,40 @@ +//nolint:revive +package xtime + +import ( + "time" +) + +func NewFakeClock() *FakeClock { + return NewFakeClockAt(time.Now()) +} + +func NewFakeClockAt(t time.Time) *FakeClock { + return &FakeClock{ + time: t, + } +} + +type FakeClock struct { + _ noCopy + + // Not protected by a mutex. If a warning is thrown in your tests, + // just disable parallel tests. + time time.Time +} + +func (c *FakeClock) Now() time.Time { + return c.time +} + +func (c *FakeClock) Since(t time.Time) time.Duration { + return c.time.Sub(t) +} + +func (c *FakeClock) Until(t time.Time) time.Duration { + return t.Sub(c.time) +} + +func (c *FakeClock) Sleep(d time.Duration) { + c.time = c.time.Add(d) +} diff --git a/vendor/github.com/samber/lo/internal/xtime/noCopy.go b/vendor/github.com/samber/lo/internal/xtime/noCopy.go new file mode 100644 index 0000000..9d425ad --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xtime/noCopy.go @@ -0,0 +1,14 @@ +package xtime + +// noCopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} diff --git a/vendor/github.com/samber/lo/internal/xtime/real.go b/vendor/github.com/samber/lo/internal/xtime/real.go new file mode 100644 index 0000000..df5a8db --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xtime/real.go @@ -0,0 +1,30 @@ +//nolint:revive +package xtime + +import ( + "time" +) + +func NewRealClock() *RealClock { + return &RealClock{} +} + +type RealClock struct { + _ noCopy +} + +func (c *RealClock) Now() time.Time { + return time.Now() +} + +func (c *RealClock) Since(t time.Time) time.Duration { + return time.Since(t) +} + +func (c *RealClock) Until(t time.Time) time.Duration { + return time.Until(t) +} + +func (c *RealClock) Sleep(d time.Duration) { + time.Sleep(d) +} diff --git a/vendor/github.com/samber/lo/internal/xtime/time.go b/vendor/github.com/samber/lo/internal/xtime/time.go new file mode 100644 index 0000000..093a959 --- /dev/null +++ b/vendor/github.com/samber/lo/internal/xtime/time.go @@ -0,0 +1,33 @@ +//nolint:revive +package xtime + +import "time" + +var clock Clock = &RealClock{} + +func SetClock(c Clock) { + clock = c +} + +func Now() time.Time { + return clock.Now() +} + +func Since(t time.Time) time.Duration { + return clock.Since(t) +} + +func Until(t time.Time) time.Duration { + return clock.Until(t) +} + +func Sleep(d time.Duration) { + clock.Sleep(d) +} + +type Clock interface { + Now() time.Time + Since(t time.Time) time.Duration + Until(t time.Time) time.Duration + Sleep(d time.Duration) +} diff --git a/vendor/github.com/samber/lo/intersect.go b/vendor/github.com/samber/lo/intersect.go index c57bd81..6c79f99 100644 --- a/vendor/github.com/samber/lo/intersect.go +++ b/vendor/github.com/samber/lo/intersect.go @@ -1,6 +1,7 @@ package lo // Contains returns true if an element is present in a collection. +// Play: https://go.dev/play/p/W1EvyqY6t9j func Contains[T comparable](collection []T, element T) bool { for i := range collection { if collection[i] == element { @@ -12,6 +13,7 @@ func Contains[T comparable](collection []T, element T) bool { } // ContainsBy returns true if predicate function return true. +// Play: https://go.dev/play/p/W1EvyqY6t9j func ContainsBy[T any](collection []T, predicate func(item T) bool) bool { for i := range collection { if predicate(collection[i]) { @@ -22,8 +24,9 @@ func ContainsBy[T any](collection []T, predicate func(item T) bool) bool { return false } -// Every returns true if all elements of a subset are contained into a collection or if the subset is empty. -func Every[T comparable](collection []T, subset []T) bool { +// Every returns true if all elements of a subset are contained in a collection or if the subset is empty. +// Play: https://go.dev/play/p/W1EvyqY6t9j +func Every[T comparable](collection, subset []T) bool { for i := range subset { if !Contains(collection, subset[i]) { return false @@ -34,6 +37,7 @@ func Every[T comparable](collection []T, subset []T) bool { } // EveryBy returns true if the predicate returns true for all elements in the collection or if the collection is empty. +// Play: https://go.dev/play/p/dn1-vhHsq9x func EveryBy[T any](collection []T, predicate func(item T) bool) bool { for i := range collection { if !predicate(collection[i]) { @@ -44,9 +48,10 @@ func EveryBy[T any](collection []T, predicate func(item T) bool) bool { return true } -// Some returns true if at least 1 element of a subset is contained into a collection. +// Some returns true if at least 1 element of a subset is contained in a collection. // If the subset is empty Some returns false. -func Some[T comparable](collection []T, subset []T) bool { +// Play: https://go.dev/play/p/Lj4ceFkeT9V +func Some[T comparable](collection, subset []T) bool { for i := range subset { if Contains(collection, subset[i]) { return true @@ -58,6 +63,7 @@ func Some[T comparable](collection []T, subset []T) bool { // SomeBy returns true if the predicate returns true for any of the elements in the collection. // If the collection is empty SomeBy returns false. +// Play: https://go.dev/play/p/DXF-TORBudx func SomeBy[T any](collection []T, predicate func(item T) bool) bool { for i := range collection { if predicate(collection[i]) { @@ -68,8 +74,9 @@ func SomeBy[T any](collection []T, predicate func(item T) bool) bool { return false } -// None returns true if no element of a subset are contained into a collection or if the subset is empty. -func None[T comparable](collection []T, subset []T) bool { +// None returns true if no element of a subset is contained in a collection or if the subset is empty. +// Play: https://go.dev/play/p/fye7JsmxzPV +func None[T comparable](collection, subset []T) bool { for i := range subset { if Contains(collection, subset[i]) { return false @@ -80,6 +87,7 @@ func None[T comparable](collection []T, subset []T) bool { } // NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty. +// Play: https://go.dev/play/p/O64WZ32H58S func NoneBy[T any](collection []T, predicate func(item T) bool) bool { for i := range collection { if predicate(collection[i]) { @@ -91,7 +99,8 @@ func NoneBy[T any](collection []T, predicate func(item T) bool) bool { } // Intersect returns the intersection between two collections. -func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice { +// Play: https://go.dev/play/p/uuElL9X9e58 +func Intersect[T comparable, Slice ~[]T](list1, list2 Slice) Slice { result := Slice{} seen := map[T]struct{}{} @@ -109,9 +118,10 @@ func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice { } // Difference returns the difference between two collections. -// The first value is the collection of element absent of list2. -// The second value is the collection of element absent of list1. -func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slice) { +// The first value is the collection of elements absent from list2. +// The second value is the collection of elements absent from list1. +// Play: https://go.dev/play/p/pKE-JgzqRpz +func Difference[T comparable, Slice ~[]T](list1, list2 Slice) (Slice, Slice) { left := Slice{} right := Slice{} @@ -143,6 +153,7 @@ func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slic // Union returns all distinct elements from given collections. // result returns will not change the order of elements relatively. +// Play: https://go.dev/play/p/DI9RVEB_qMK func Union[T comparable, Slice ~[]T](lists ...Slice) Slice { var capLen int @@ -165,7 +176,8 @@ func Union[T comparable, Slice ~[]T](lists ...Slice) Slice { return result } -// Without returns slice excluding all given values. +// Without returns a slice excluding all given values. +// Play: https://go.dev/play/p/5j30Ux8TaD0 func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice { excludeMap := make(map[T]struct{}, len(exclude)) for i := range exclude { @@ -182,14 +194,15 @@ func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice { } // WithoutBy filters a slice by excluding elements whose extracted keys match any in the exclude list. -// It returns a new slice containing only the elements whose keys are not in the exclude list. -func WithoutBy[T any, K comparable](collection []T, iteratee func(item T) K, exclude ...K) []T { +// Returns a new slice containing only the elements whose keys are not in the exclude list. +// Play: https://go.dev/play/p/VgWJOF01NbJ +func WithoutBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) K, exclude ...K) Slice { excludeMap := make(map[K]struct{}, len(exclude)) for _, e := range exclude { excludeMap[e] = struct{}{} } - result := make([]T, 0, len(collection)) + result := make(Slice, 0, len(collection)) for _, item := range collection { if _, ok := excludeMap[iteratee(item)]; !ok { result = append(result, item) @@ -198,14 +211,15 @@ func WithoutBy[T any, K comparable](collection []T, iteratee func(item T) K, exc return result } -// WithoutEmpty returns slice excluding zero values. +// WithoutEmpty returns a slice excluding zero values. // // Deprecated: Use lo.Compact instead. func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) Slice { return Compact(collection) } -// WithoutNth returns slice excluding nth value. +// WithoutNth returns a slice excluding the nth value. +// Play: https://go.dev/play/p/5g3F9R2H1xL func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) Slice { length := len(collection) @@ -227,16 +241,18 @@ func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) Slice { } // ElementsMatch returns true if lists contain the same set of elements (including empty set). -// If there are duplicate elements, the number of appearances of each of them in both lists should match. +// If there are duplicate elements, the number of occurrences in each list should match. // The order of elements is not checked. -func ElementsMatch[T comparable, Slice ~[]T](list1 Slice, list2 Slice) bool { +// Play: https://go.dev/play/p/XWSEM4Ic_t0 +func ElementsMatch[T comparable, Slice ~[]T](list1, list2 Slice) bool { return ElementsMatchBy(list1, list2, func(item T) T { return item }) } // ElementsMatchBy returns true if lists contain the same set of elements' keys (including empty set). -// If there are duplicate keys, the number of appearances of each of them in both lists should match. +// If there are duplicate keys, the number of occurrences in each list should match. // The order of elements is not checked. -func ElementsMatchBy[T any, K comparable](list1 []T, list2 []T, iteratee func(item T) K) bool { +// Play: https://go.dev/play/p/XWSEM4Ic_t0 +func ElementsMatchBy[T any, K comparable](list1, list2 []T, iteratee func(item T) K) bool { if len(list1) != len(list2) { return false } diff --git a/vendor/github.com/samber/lo/map.go b/vendor/github.com/samber/lo/map.go index 6febb3f..cd0e9d7 100644 --- a/vendor/github.com/samber/lo/map.go +++ b/vendor/github.com/samber/lo/map.go @@ -1,6 +1,6 @@ package lo -// Keys creates an array of the map keys. +// Keys creates a slice of the map keys. // Play: https://go.dev/play/p/Uu11fHASqrU func Keys[K comparable, V any](in ...map[K]V) []K { size := 0 @@ -18,7 +18,7 @@ func Keys[K comparable, V any](in ...map[K]V) []K { return result } -// UniqKeys creates an array of unique keys in the map. +// UniqKeys creates a slice of unique keys in the map. // Play: https://go.dev/play/p/TPKAb6ILdHk func UniqKeys[K comparable, V any](in ...map[K]V) []K { size := 0 @@ -49,7 +49,7 @@ func HasKey[K comparable, V any](in map[K]V, key K) bool { return ok } -// Values creates an array of the map values. +// Values creates a slice of the map values. // Play: https://go.dev/play/p/nnRTQkzQfF6 func Values[K comparable, V any](in ...map[K]V) []V { size := 0 @@ -59,17 +59,17 @@ func Values[K comparable, V any](in ...map[K]V) []V { result := make([]V, 0, size) for i := range in { - for k := range in[i] { - result = append(result, in[i][k]) + for _, v := range in[i] { + result = append(result, v) } } return result } -// UniqValues creates an array of unique values in the map. +// UniqValues creates a slice of unique values in the map. // Play: https://go.dev/play/p/nf6bXMh7rM3 -func UniqValues[K comparable, V comparable](in ...map[K]V) []V { +func UniqValues[K, V comparable](in ...map[K]V) []V { size := 0 for i := range in { size += len(in[i]) @@ -79,13 +79,12 @@ func UniqValues[K comparable, V comparable](in ...map[K]V) []V { result := make([]V, 0) for i := range in { - for k := range in[i] { - val := in[i][k] - if _, exists := seen[val]; exists { + for _, v := range in[i] { + if _, exists := seen[v]; exists { continue } - seen[val] = struct{}{} - result = append(result, val) + seen[v] = struct{}{} + result = append(result, v) } } @@ -105,9 +104,9 @@ func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) V { // Play: https://go.dev/play/p/kdg8GR_QMmf func PickBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map { r := Map{} - for k := range in { - if predicate(k, in[k]) { - r[k] = in[k] + for k, v := range in { + if predicate(k, v) { + r[k] = v } } return r @@ -127,11 +126,11 @@ func PickByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map { // PickByValues returns same map type filtered by given values. // Play: https://go.dev/play/p/1zdzSvbfsJc -func PickByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map { +func PickByValues[K, V comparable, Map ~map[K]V](in Map, values []V) Map { r := Map{} - for k := range in { - if Contains(values, in[k]) { - r[k] = in[k] + for k, v := range in { + if Contains(values, v) { + r[k] = v } } return r @@ -141,9 +140,9 @@ func PickByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) // Play: https://go.dev/play/p/EtBsR43bdsd func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map { r := Map{} - for k := range in { - if !predicate(k, in[k]) { - r[k] = in[k] + for k, v := range in { + if !predicate(k, v) { + r[k] = v } } return r @@ -153,8 +152,8 @@ func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, val // Play: https://go.dev/play/p/t1QjCrs-ysk func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map { r := Map{} - for k := range in { - r[k] = in[k] + for k, v := range in { + r[k] = v } for i := range keys { delete(r, keys[i]) @@ -164,39 +163,39 @@ func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map { // OmitByValues returns same map type filtered by given values. // Play: https://go.dev/play/p/9UYZi-hrs8j -func OmitByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map { +func OmitByValues[K, V comparable, Map ~map[K]V](in Map, values []V) Map { r := Map{} - for k := range in { - if !Contains(values, in[k]) { - r[k] = in[k] + for k, v := range in { + if !Contains(values, v) { + r[k] = v } } return r } -// Entries transforms a map into array of key/value pairs. -// Play: +// Entries transforms a map into a slice of key/value pairs. +// Play: https://go.dev/play/p/_t4Xe34-Nl5 func Entries[K comparable, V any](in map[K]V) []Entry[K, V] { entries := make([]Entry[K, V], 0, len(in)) - for k := range in { + for k, v := range in { entries = append(entries, Entry[K, V]{ Key: k, - Value: in[k], + Value: v, }) } return entries } -// ToPairs transforms a map into array of key/value pairs. +// ToPairs transforms a map into a slice of key/value pairs. // Alias of Entries(). // Play: https://go.dev/play/p/3Dhgx46gawJ func ToPairs[K comparable, V any](in map[K]V) []Entry[K, V] { return Entries(in) } -// FromEntries transforms an array of key/value pairs into a map. +// FromEntries transforms a slice of key/value pairs into a map. // Play: https://go.dev/play/p/oIr5KHFGCEN func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V { out := make(map[K]V, len(entries)) @@ -208,7 +207,7 @@ func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V { return out } -// FromPairs transforms an array of key/value pairs into a map. +// FromPairs transforms a slice of key/value pairs into a map. // Alias of FromEntries(). // Play: https://go.dev/play/p/oIr5KHFGCEN func FromPairs[K comparable, V any](entries []Entry[K, V]) map[K]V { @@ -219,11 +218,11 @@ func FromPairs[K comparable, V any](entries []Entry[K, V]) map[K]V { // contains duplicate values, subsequent values overwrite property assignments // of previous values. // Play: https://go.dev/play/p/rFQ4rak6iA1 -func Invert[K comparable, V comparable](in map[K]V) map[V]K { +func Invert[K, V comparable](in map[K]V) map[V]K { out := make(map[V]K, len(in)) - for k := range in { - out[in[k]] = k + for k, v := range in { + out[v] = k } return out @@ -239,20 +238,20 @@ func Assign[K comparable, V any, Map ~map[K]V](maps ...Map) Map { out := make(Map, count) for i := range maps { - for k := range maps[i] { - out[k] = maps[i][k] + for k, v := range maps[i] { + out[k] = v } } return out } -// ChunkEntries splits a map into an array of elements in groups of a length equal to its size. If the map cannot be split evenly, +// ChunkEntries splits a map into a slice of elements in groups of length equal to its size. If the map cannot be split evenly, // the final chunk will contain the remaining elements. // Play: https://go.dev/play/p/X_YQL6mmoD- func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V { if size <= 0 { - panic("The chunk size must be greater than 0") + panic("lo.ChunkEntries: size must be greater than 0") } count := len(m) @@ -262,7 +261,7 @@ func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V { chunksNum := count / size if count%size != 0 { - chunksNum += 1 + chunksNum++ } result := make([]map[K]V, 0, chunksNum) @@ -278,31 +277,31 @@ func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V { return result } -// MapKeys manipulates a map keys and transforms it to a map of another type. +// MapKeys manipulates map keys and transforms it to a map of another type. // Play: https://go.dev/play/p/9_4WPIqOetJ func MapKeys[K comparable, V any, R comparable](in map[K]V, iteratee func(value V, key K) R) map[R]V { result := make(map[R]V, len(in)) - for k := range in { - result[iteratee(in[k], k)] = in[k] + for k, v := range in { + result[iteratee(v, k)] = v } return result } -// MapValues manipulates a map values and transforms it to a map of another type. +// MapValues manipulates map values and transforms it to a map of another type. // Play: https://go.dev/play/p/T_8xAfvcf0W -func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(value V, key K) R) map[K]R { +func MapValues[K comparable, V, R any](in map[K]V, iteratee func(value V, key K) R) map[K]R { result := make(map[K]R, len(in)) - for k := range in { - result[k] = iteratee(in[k], k) + for k, v := range in { + result[k] = iteratee(v, k) } return result } -// MapEntries manipulates a map entries and transforms it to a map of another type. +// MapEntries manipulates map entries and transforms it to a map of another type. // Play: https://go.dev/play/p/VuvNQzxKimT func MapEntries[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 { result := make(map[K2]V2, len(in)) @@ -315,27 +314,58 @@ func MapEntries[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iter return result } -// MapToSlice transforms a map into a slice based on specific iteratee +// MapToSlice transforms a map into a slice based on specified iteratee. // Play: https://go.dev/play/p/ZuiCZpDt6LD -func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) R) []R { +func MapToSlice[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) R) []R { result := make([]R, 0, len(in)) - for k := range in { - result = append(result, iteratee(k, in[k])) + for k, v := range in { + result = append(result, iteratee(k, v)) } return result } -// FilterMapToSlice transforms a map into a slice based on specific iteratee. +// FilterMapToSlice transforms a map into a slice based on specified iteratee. // The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice. // If the boolean is false, the value is not added to the result slice. // The order of the keys in the input map is not specified and the order of the keys in the output slice is not guaranteed. -func FilterMapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) (R, bool)) []R { +// Play: https://go.dev/play/p/jgsD_Kil9pV +func FilterMapToSlice[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) (R, bool)) []R { result := make([]R, 0, len(in)) - for k := range in { - if v, ok := iteratee(k, in[k]); ok { + for k, v := range in { + if v, ok := iteratee(k, v); ok { + result = append(result, v) + } + } + + return result +} + +// FilterKeys transforms a map into a slice based on predicate returns true for specific elements. +// It is a mix of lo.Filter() and lo.Keys(). +// Play: https://go.dev/play/p/OFlKXlPrBAe +func FilterKeys[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) []K { + result := make([]K, 0) + + for k, v := range in { + if predicate(k, v) { + result = append(result, k) + } + } + + return result +} + +// FilterValues transforms a map into a slice based on predicate returns true for specific elements. +// It is a mix of lo.Filter() and lo.Values(). +// Play: https://go.dev/play/p/YVD5r_h-LX- +func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) []V { + result := make([]V, 0) + + for k, v := range in { + if predicate(k, v) { result = append(result, v) } } diff --git a/vendor/github.com/samber/lo/math.go b/vendor/github.com/samber/lo/math.go index e3f4289..9342b59 100644 --- a/vendor/github.com/samber/lo/math.go +++ b/vendor/github.com/samber/lo/math.go @@ -4,7 +4,7 @@ import ( "github.com/samber/lo/internal/constraints" ) -// Range creates an array of numbers (positive and/or negative) with given length. +// Range creates a slice of numbers (positive and/or negative) with given length. // Play: https://go.dev/play/p/0r6VimXAi9H func Range(elementNum int) []int { length := If(elementNum < 0, -elementNum).Else(elementNum) @@ -16,7 +16,7 @@ func Range(elementNum int) []int { return result } -// RangeFrom creates an array of numbers from start with specified length. +// RangeFrom creates a slice of numbers from start with specified length. // Play: https://go.dev/play/p/0r6VimXAi9H func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T { length := If(elementNum < 0, -elementNum).Else(elementNum) @@ -28,8 +28,8 @@ func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum in return result } -// RangeWithSteps creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. -// step set to zero will return empty array. +// RangeWithSteps creates a slice of numbers (positive and/or negative) progressing from start up to, but not including end. +// step set to zero will return an empty slice. // Play: https://go.dev/play/p/0r6VimXAi9H func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T { result := []T{} @@ -56,11 +56,11 @@ func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step // Clamp clamps number within the inclusive lower and upper bounds. // Play: https://go.dev/play/p/RU4lJNC2hlI -func Clamp[T constraints.Ordered](value T, min T, max T) T { - if value < min { - return min - } else if value > max { - return max +func Clamp[T constraints.Ordered](value, mIn, mAx T) T { + if value < mIn { + return mIn + } else if value > mAx { + return mAx } return value } @@ -68,7 +68,7 @@ func Clamp[T constraints.Ordered](value T, min T, max T) T { // Sum sums the values in a collection. If collection is empty 0 is returned. // Play: https://go.dev/play/p/upfeJVqs4Bt func Sum[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) T { - var sum T = 0 + var sum T for i := range collection { sum += collection[i] } @@ -78,14 +78,14 @@ func Sum[T constraints.Float | constraints.Integer | constraints.Complex](collec // SumBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 is returned. // Play: https://go.dev/play/p/Dz_a_7jN_ca func SumBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) R { - var sum R = 0 + var sum R for i := range collection { - sum = sum + iteratee(collection[i]) + sum += iteratee(collection[i]) } return sum } -// Product gets the product of the values in a collection. If collection is empty 0 is returned. +// Product gets the product of the values in a collection. If collection is empty 1 is returned. // Play: https://go.dev/play/p/2_kjM_smtAH func Product[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) T { if collection == nil { @@ -103,7 +103,7 @@ func Product[T constraints.Float | constraints.Integer | constraints.Complex](co return product } -// ProductBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 is returned. +// ProductBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 1 is returned. // Play: https://go.dev/play/p/wadzrWr9Aer func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) R { if collection == nil { @@ -116,27 +116,57 @@ func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Co var product R = 1 for i := range collection { - product = product * iteratee(collection[i]) + product *= iteratee(collection[i]) } return product } // Mean calculates the mean of a collection of numbers. +// Play: https://go.dev/play/p/tPURSuteUsP func Mean[T constraints.Float | constraints.Integer](collection []T) T { - var length = T(len(collection)) + length := T(len(collection)) if length == 0 { return 0 } - var sum = Sum(collection) + sum := Sum(collection) return sum / length } // MeanBy calculates the mean of a collection of numbers using the given return value from the iteration function. +// Play: https://go.dev/play/p/j7TsVwBOZ7P func MeanBy[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) R) R { - var length = R(len(collection)) + length := R(len(collection)) if length == 0 { return 0 } - var sum = SumBy(collection, iteratee) + sum := SumBy(collection, iteratee) return sum / length } + +// Mode returns the mode (most frequent value) of a collection. +// If multiple values have the same highest frequency, then multiple values are returned. +// If the collection is empty, then the zero value of T is returned. +func Mode[T constraints.Integer | constraints.Float](collection []T) []T { + length := T(len(collection)) + if length == 0 { + return []T{} + } + + mode := make([]T, 0) + maxFreq := 0 + frequency := make(map[T]int) + + for _, item := range collection { + frequency[item]++ + count := frequency[item] + + if count > maxFreq { + maxFreq = count + mode = []T{item} + } else if count == maxFreq { + mode = append(mode, item) + } + } + + return mode +} diff --git a/vendor/github.com/samber/lo/mutable/slice.go b/vendor/github.com/samber/lo/mutable/slice.go index 969f399..f3412c1 100644 --- a/vendor/github.com/samber/lo/mutable/slice.go +++ b/vendor/github.com/samber/lo/mutable/slice.go @@ -1,12 +1,13 @@ package mutable -import "github.com/samber/lo/internal/rand" +import "github.com/samber/lo/internal/xrand" // Filter is a generic function that modifies the input slice in-place to contain only the elements // that satisfy the provided predicate function. The predicate function takes an element of the slice and its index, // and should return true for elements that should be kept and false for elements that should be removed. // The function returns the modified slice, which may be shorter than the original if some elements were removed. // Note that the order of elements in the original slice is preserved in the output. +// Play: https://go.dev/play/p/0jY3Z0B7O_5 func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice { j := 0 for _, item := range collection { @@ -36,6 +37,7 @@ func FilterI[T any, Slice ~[]T](collection Slice, predicate func(item T, index i // Map is a generic function that modifies the input slice in-place to contain the result of applying the provided // function to each element of the slice. The function returns the modified slice, which has the same length as the original. +// Play: https://go.dev/play/p/0jY3Z0B7O_5 func Map[T any, Slice ~[]T](collection Slice, fn func(item T) T) { for i := range collection { collection[i] = fn(collection[i]) @@ -50,21 +52,21 @@ func MapI[T any, Slice ~[]T](collection Slice, fn func(item T, index int) T) { } } -// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm. +// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm. // Play: https://go.dev/play/p/2xb3WdLjeSJ func Shuffle[T any, Slice ~[]T](collection Slice) { - rand.Shuffle(len(collection), func(i, j int) { + xrand.Shuffle(len(collection), func(i, j int) { collection[i], collection[j] = collection[j], collection[i] }) } -// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on. +// Reverse reverses a slice so that the first element becomes the last, the second element becomes the second to last, and so on. // Play: https://go.dev/play/p/O-M5pmCRgzV func Reverse[T any, Slice ~[]T](collection Slice) { length := len(collection) half := length / 2 - for i := 0; i < half; i = i + 1 { + for i := 0; i < half; i++ { j := length - 1 - i collection[i], collection[j] = collection[j], collection[i] } diff --git a/vendor/github.com/samber/lo/retry.go b/vendor/github.com/samber/lo/retry.go index 5b9cef3..e8b98c4 100644 --- a/vendor/github.com/samber/lo/retry.go +++ b/vendor/github.com/samber/lo/retry.go @@ -3,6 +3,8 @@ package lo import ( "sync" "time" + + "github.com/samber/lo/internal/xtime" ) type debounce struct { @@ -26,8 +28,13 @@ func (d *debounce) reset() { } d.timer = time.AfterFunc(d.after, func() { - for i := range d.callbacks { - d.callbacks[i]() + // We need to lock the mutex here to avoid race conditions with 2 concurrent calls to reset() + d.mu.Lock() + callbacks := append([]func(){}, d.callbacks...) + d.mu.Unlock() + + for i := range callbacks { + callbacks[i]() } }) } @@ -96,13 +103,15 @@ func (d *debounceBy[T]) reset(key T) { } item.timer = time.AfterFunc(d.after, func() { + // We need to lock the mutex here to avoid race conditions with 2 concurrent calls to reset() item.mu.Lock() count := item.count item.count = 0 + callbacks := append([]func(key T, count int){}, d.callbacks...) item.mu.Unlock() - for i := range d.callbacks { - d.callbacks[i](key, count) + for i := range callbacks { + callbacks[i](key, count) } }) } @@ -165,20 +174,20 @@ func Attempt(maxIteration int, f func(index int) error) (int, error) { func AttemptWithDelay(maxIteration int, delay time.Duration, f func(index int, duration time.Duration) error) (int, time.Duration, error) { var err error - start := time.Now() + start := xtime.Now() for i := 0; maxIteration <= 0 || i < maxIteration; i++ { - err = f(i, time.Since(start)) + err = f(i, xtime.Since(start)) if err == nil { - return i + 1, time.Since(start), nil + return i + 1, xtime.Since(start), nil } if maxIteration <= 0 || i+1 < maxIteration { - time.Sleep(delay) + xtime.Sleep(delay) } } - return maxIteration, time.Since(start), err + return maxIteration, xtime.Since(start), err } // AttemptWhile invokes a function N times until it returns valid output. @@ -187,6 +196,7 @@ func AttemptWithDelay(maxIteration int, delay time.Duration, f func(index int, d // immediately if the second return value is false. When the first // argument is less than `1`, the function runs until a successful response is // returned. +// Play: https://go.dev/play/p/1VS7HxlYMOG func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) { var err error var shouldContinueInvoke bool @@ -211,27 +221,28 @@ func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) { // It will terminate the invoke immediately if the second return value is false. // When the first argument is less than `1`, the function runs until a successful // response is returned. +// Play: https://go.dev/play/p/mhufUjJfLEF func AttemptWhileWithDelay(maxIteration int, delay time.Duration, f func(int, time.Duration) (error, bool)) (int, time.Duration, error) { var err error var shouldContinueInvoke bool - start := time.Now() + start := xtime.Now() for i := 0; maxIteration <= 0 || i < maxIteration; i++ { - err, shouldContinueInvoke = f(i, time.Since(start)) + err, shouldContinueInvoke = f(i, xtime.Since(start)) if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately - return i + 1, time.Since(start), err + return i + 1, xtime.Since(start), err } if err == nil { - return i + 1, time.Since(start), nil + return i + 1, xtime.Since(start), nil } if maxIteration <= 0 || i+1 < maxIteration { - time.Sleep(delay) + xtime.Sleep(delay) } } - return maxIteration, time.Since(start), err + return maxIteration, xtime.Since(start), err } type transactionStep[T any] struct { @@ -240,18 +251,20 @@ type transactionStep[T any] struct { } // NewTransaction instantiate a new transaction. +// Play: https://go.dev/play/p/Qxrd7MGQGh1 func NewTransaction[T any]() *Transaction[T] { return &Transaction[T]{ steps: []transactionStep[T]{}, } } -// Transaction implements a Saga pattern +// Transaction implements a Saga pattern. type Transaction[T any] struct { steps []transactionStep[T] } -// Then adds a step to the chain of callbacks. It returns the same Transaction. +// Then adds a step to the chain of callbacks. Returns the same Transaction. +// Play: https://go.dev/play/p/Qxrd7MGQGh1 https://go.dev/play/p/xrHb2_kMvTY func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Transaction[T] { t.steps = append(t.steps, transactionStep[T]{ exec: exec, @@ -262,6 +275,7 @@ func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Tr } // Process runs the Transaction steps and rollbacks in case of errors. +// Play: https://go.dev/play/p/Qxrd7MGQGh1 https://go.dev/play/p/xrHb2_kMvTY func (t *Transaction[T]) Process(state T) (T, error) { var i int var err error @@ -287,7 +301,7 @@ func (t *Transaction[T]) Process(state T) (T, error) { return state, err } -// @TODO: single mutex per key ? +// @TODO: single mutex per key? type throttleBy[T comparable] struct { mu *sync.Mutex timer *time.Timer @@ -311,7 +325,6 @@ func (th *throttleBy[T]) throttledFunc(key T) { for _, f := range th.callbacks { f(key) } - } if th.timer == nil { th.timer = time.AfterFunc(th.interval, func() { @@ -333,13 +346,15 @@ func (th *throttleBy[T]) reset() { } // NewThrottle creates a throttled instance that invokes given functions only once in every interval. -// This returns 2 functions, First one is throttled function and Second one is a function to reset interval -func NewThrottle(interval time.Duration, f ...func()) (throttle func(), reset func()) { +// This returns 2 functions, First one is throttled function and Second one is a function to reset interval. +// Play: https://go.dev/play/p/qQn3fm8Z7jS +func NewThrottle(interval time.Duration, f ...func()) (throttle, reset func()) { return NewThrottleWithCount(interval, 1, f...) } // NewThrottleWithCount is NewThrottle with count limit, throttled function will be invoked count times in every interval. -func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throttle func(), reset func()) { +// Play: https://go.dev/play/p/w5nc0MgWtjC +func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throttle, reset func()) { callbacks := Map(f, func(item func(), _ int) func(struct{}) { return func(struct{}) { item() @@ -353,12 +368,14 @@ func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throt } // NewThrottleBy creates a throttled instance that invokes given functions only once in every interval. -// This returns 2 functions, First one is throttled function and Second one is a function to reset interval +// This returns 2 functions, First one is throttled function and Second one is a function to reset interval. +// Play: https://go.dev/play/p/0Wv6oX7dHdC func NewThrottleBy[T comparable](interval time.Duration, f ...func(key T)) (throttle func(key T), reset func()) { return NewThrottleByWithCount[T](interval, 1, f...) } // NewThrottleByWithCount is NewThrottleBy with count limit, throttled function will be invoked count times in every interval. +// Play: https://go.dev/play/p/vQk3ECH7_EW func NewThrottleByWithCount[T comparable](interval time.Duration, count int, f ...func(key T)) (throttle func(key T), reset func()) { if count <= 0 { count = 1 diff --git a/vendor/github.com/samber/lo/slice.go b/vendor/github.com/samber/lo/slice.go index 9c15a6c..d35d276 100644 --- a/vendor/github.com/samber/lo/slice.go +++ b/vendor/github.com/samber/lo/slice.go @@ -7,7 +7,7 @@ import ( "github.com/samber/lo/mutable" ) -// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for. +// Filter iterates over elements of collection, returning a slice of all elements predicate returns true for. // Play: https://go.dev/play/p/Apjg3WeSi7K func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice { result := make(Slice, 0, len(collection)) @@ -23,7 +23,7 @@ func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index in // Map manipulates a slice and transforms it to a slice of another type. // Play: https://go.dev/play/p/OkPcYAhBo0D -func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R { +func Map[T, R any](collection []T, iteratee func(item T, index int) R) []R { result := make([]R, len(collection)) for i := range collection { @@ -34,6 +34,7 @@ func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R { } // UniqMap manipulates a slice and transforms it to a slice of another type with unique values. +// Play: https://go.dev/play/p/fygzLBhvUdB func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index int) R) []R { result := make([]R, 0, len(collection)) seen := make(map[R]struct{}, len(collection)) @@ -48,13 +49,13 @@ func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index in return result } -// FilterMap returns a slice which obtained after both filtering and mapping using the given callback function. +// FilterMap returns a slice obtained after both filtering and mapping using the given callback function. // The callback function should return two values: // - the result of the mapping operation and // - whether the result element should be included or not. // -// Play: https://go.dev/play/p/-AuYXfy7opz -func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R { +// Play: https://go.dev/play/p/CgHYNUpOd1I +func FilterMap[T, R any](collection []T, callback func(item T, index int) (R, bool)) []R { result := make([]R, 0, len(collection)) for i := range collection { @@ -69,8 +70,8 @@ func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R // FlatMap manipulates a slice and transforms and flattens it to a slice of another type. // The transform function can either return a slice or a `nil`, and in the `nil` case // no value is added to the final slice. -// Play: https://go.dev/play/p/YSoYmQTA8-U -func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R) []R { +// Play: https://go.dev/play/p/pFCF5WVB225 +func FlatMap[T, R any](collection []T, iteratee func(item T, index int) []R) []R { result := make([]R, 0, len(collection)) for i := range collection { @@ -82,8 +83,8 @@ func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R) // Reduce reduces collection to a value which is the accumulated result of running each element in collection // through accumulator, where each successive invocation is supplied the return value of the previous. -// Play: https://go.dev/play/p/R4UHXZNaaUG -func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { +// Play: https://go.dev/play/p/CgHYNUpOd1I +func Reduce[T, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { for i := range collection { initial = accumulator(initial, collection[i], i) } @@ -91,9 +92,9 @@ func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index return initial } -// ReduceRight helper is like Reduce except that it iterates over elements of collection from right to left. +// ReduceRight is like Reduce except that it iterates over elements of collection from right to left. // Play: https://go.dev/play/p/Fq3W70l7wXF -func ReduceRight[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { +func ReduceRight[T, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R { for i := len(collection) - 1; i >= 0; i-- { initial = accumulator(initial, collection[i], i) } @@ -112,7 +113,7 @@ func ForEach[T any](collection []T, iteratee func(item T, index int)) { // ForEachWhile iterates over elements of collection and invokes iteratee for each element // collection return value decide to continue or break, like do while(). // Play: https://go.dev/play/p/QnLGt35tnow -func ForEachWhile[T any](collection []T, iteratee func(item T, index int) (goon bool)) { +func ForEachWhile[T any](collection []T, iteratee func(item T, index int) bool) { for i := range collection { if !iteratee(collection[i], i) { break @@ -120,7 +121,7 @@ func ForEachWhile[T any](collection []T, iteratee func(item T, index int) (goon } } -// Times invokes the iteratee n times, returning an array of the results of each invocation. +// Times invokes the iteratee n times, returning a slice of the results of each invocation. // The iteratee is invoked with index as argument. // Play: https://go.dev/play/p/vgQj3Glr6lT func Times[T any](count int, iteratee func(index int) T) []T { @@ -133,8 +134,8 @@ func Times[T any](count int, iteratee func(index int) T) []T { return result } -// Uniq returns a duplicate-free version of an array, in which only the first occurrence of each element is kept. -// The order of result values is determined by the order they occur in the array. +// Uniq returns a duplicate-free version of a slice, in which only the first occurrence of each element is kept. +// The order of result values is determined by the order they occur in the slice. // Play: https://go.dev/play/p/DTzbeXZ6iEN func Uniq[T comparable, Slice ~[]T](collection Slice) Slice { result := make(Slice, 0, len(collection)) @@ -152,9 +153,9 @@ func Uniq[T comparable, Slice ~[]T](collection Slice) Slice { return result } -// UniqBy returns a duplicate-free version of an array, in which only the first occurrence of each element is kept. -// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is -// invoked for each element in array to generate the criterion by which uniqueness is computed. +// UniqBy returns a duplicate-free version of a slice, in which only the first occurrence of each element is kept. +// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is +// invoked for each element in the slice to generate the criterion by which uniqueness is computed. // Play: https://go.dev/play/p/g42Z3QSb53u func UniqBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice { result := make(Slice, 0, len(collection)) @@ -189,6 +190,7 @@ func GroupBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(it } // GroupByMap returns an object composed of keys generated from the results of running each element of collection through iteratee. +// Play: https://go.dev/play/p/iMeruQ3_W80 func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T) (K, V)) map[K][]V { result := map[K][]V{} @@ -201,17 +203,17 @@ func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T return result } -// Chunk returns an array of elements split into groups the length of size. If array can't be split evenly, +// Chunk returns a slice of elements split into groups of length size. If the slice can't be split evenly, // the final chunk will be the remaining elements. -// Play: https://go.dev/play/p/EeKl0AuTehH +// Play: https://go.dev/play/p/kEMkFbdu85g func Chunk[T any, Slice ~[]T](collection Slice, size int) []Slice { if size <= 0 { - panic("Second parameter must be greater than 0") + panic("lo.Chunk: size must be greater than 0") } chunksNum := len(collection) / size if len(collection)%size != 0 { - chunksNum += 1 + chunksNum++ } result := make([]Slice, 0, chunksNum) @@ -221,13 +223,17 @@ func Chunk[T any, Slice ~[]T](collection Slice, size int) []Slice { if last > len(collection) { last = len(collection) } - result = append(result, collection[i*size:last:last]) + + // Copy chunk in a new slice, to prevent memory leak and free memory from initial collection. + newSlice := make(Slice, last-i*size) + copy(newSlice, collection[i*size:last]) + result = append(result, newSlice) } return result } -// PartitionBy returns an array of elements split into groups. The order of grouped values is +// PartitionBy returns a slice of elements split into groups. The order of grouped values is // determined by the order they occur in collection. The grouping is generated from the results // of running each element of collection through iteratee. // Play: https://go.dev/play/p/NfQ_nGjkgXW @@ -255,7 +261,7 @@ func PartitionBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee fun // return Values[K, []T](groups) } -// Flatten returns an array a single level deep. +// Flatten returns a slice a single level deep. // Play: https://go.dev/play/p/rbp9ORaMpjw func Flatten[T any, Slice ~[]T](collection []Slice) Slice { totalLen := 0 @@ -271,7 +277,7 @@ func Flatten[T any, Slice ~[]T](collection []Slice) Slice { return result } -// Interleave round-robin alternating input slices and sequentially appending value at index into result +// Interleave round-robin alternating input slices and sequentially appending value at index into result. // Play: https://go.dev/play/p/-RJkTLQEDVt func Interleave[T any, Slice ~[]T](collections ...Slice) Slice { if len(collections) == 0 { @@ -309,7 +315,7 @@ func Interleave[T any, Slice ~[]T](collections ...Slice) Slice { return result } -// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm. +// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm. // Play: https://go.dev/play/p/ZTGG7OUCdnp // // Deprecated: use mutable.Shuffle() instead. @@ -318,7 +324,7 @@ func Shuffle[T any, Slice ~[]T](collection Slice) Slice { return collection } -// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on. +// Reverse reverses a slice so that the first element becomes the last, the second element becomes the second to last, and so on. // Play: https://go.dev/play/p/iv2e9jslfBM // // Deprecated: use mutable.Reverse() instead. @@ -327,7 +333,7 @@ func Reverse[T any, Slice ~[]T](collection Slice) Slice { return collection } -// Fill fills elements of array with `initial` value. +// Fill fills elements of a slice with `initial` value. // Play: https://go.dev/play/p/VwR34GzqEub func Fill[T Clonable[T], Slice ~[]T](collection Slice, initial T) Slice { result := make(Slice, 0, len(collection)) @@ -363,8 +369,8 @@ func RepeatBy[T any](count int, predicate func(index int) T) []T { return result } -// KeyBy transforms a slice or an array of structs to a map based on a pivot callback. -// Play: https://go.dev/play/p/mdaClUAT-zZ +// KeyBy transforms a slice or a slice of structs to a map based on a pivot callback. +// Play: https://go.dev/play/p/ccUiUL_Lnel func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V { result := make(map[K]V, len(collection)) @@ -377,14 +383,24 @@ func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V } // Associate returns a map containing key-value pairs provided by transform function applied to elements of the given slice. -// If any of two pairs would have the same key the last one gets added to the map. -// The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. // Play: https://go.dev/play/p/WHa2CfMO3Lr func Associate[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K]V { + return AssociateI(collection, func(item T, _ int) (K, V) { + return transform(item) + }) +} + +// AssociateI returns a map containing key-value pairs provided by transform function applied to elements of the given slice. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. +// Play: https://go.dev/play/p/Ugmz6S22rRO +func AssociateI[T any, K comparable, V any](collection []T, transform func(item T, index int) (K, V)) map[K]V { result := make(map[K]V, len(collection)) - for i := range collection { - k, v := transform(collection[i]) + for index, item := range collection { + k, v := transform(item, index) result[k] = v } @@ -392,23 +408,44 @@ func Associate[T any, K comparable, V any](collection []T, transform func(item T } // SliceToMap returns a map containing key-value pairs provided by transform function applied to elements of the given slice. -// If any of two pairs would have the same key the last one gets added to the map. -// The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. // Alias of Associate(). // Play: https://go.dev/play/p/WHa2CfMO3Lr func SliceToMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K]V { return Associate(collection, transform) } +// SliceToMapI returns a map containing key-value pairs provided by transform function applied to elements of the given slice. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. +// Alias of AssociateI(). +// Play: https://go.dev/play/p/mMBm5GV3_eq +func SliceToMapI[T any, K comparable, V any](collection []T, transform func(item T, index int) (K, V)) map[K]V { + return AssociateI(collection, transform) +} + // FilterSliceToMap returns a map containing key-value pairs provided by transform function applied to elements of the given slice. -// If any of two pairs would have the same key the last one gets added to the map. -// The order of keys in returned map is not specified and is not guaranteed to be the same from the original array. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. // The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map. +// Play: https://go.dev/play/p/2z0rDz2ZSGU func FilterSliceToMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V, bool)) map[K]V { + return FilterSliceToMapI(collection, func(item T, _ int) (K, V, bool) { + return transform(item) + }) +} + +// FilterSliceToMapI returns a map containing key-value pairs provided by transform function applied to elements of the given slice. +// If any of two pairs have the same key the last one gets added to the map. +// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice. +// The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map. +// Play: https://go.dev/play/p/mSz_bUIk9aJ +func FilterSliceToMapI[T any, K comparable, V any](collection []T, transform func(item T, index int) (K, V, bool)) map[K]V { result := make(map[K]V, len(collection)) - for i := range collection { - k, v, ok := transform(collection[i]) + for index, item := range collection { + k, v, ok := transform(item, index) if ok { result[k] = v } @@ -418,6 +455,7 @@ func FilterSliceToMap[T any, K comparable, V any](collection []T, transform func } // Keyify returns a map with each unique element of the slice as a key. +// Play: https://go.dev/play/p/RYhhM_csqIG func Keyify[T comparable, Slice ~[]T](collection Slice) map[T]struct{} { result := make(map[T]struct{}, len(collection)) @@ -428,9 +466,13 @@ func Keyify[T comparable, Slice ~[]T](collection Slice) map[T]struct{} { return result } -// Drop drops n elements from the beginning of a slice or array. +// Drop drops n elements from the beginning of a slice. // Play: https://go.dev/play/p/JswS7vXRJP2 func Drop[T any, Slice ~[]T](collection Slice, n int) Slice { + if n < 0 { + panic("lo.Drop: n must not be negative") + } + if len(collection) <= n { return make(Slice, 0) } @@ -440,9 +482,13 @@ func Drop[T any, Slice ~[]T](collection Slice, n int) Slice { return append(result, collection[n:]...) } -// DropRight drops n elements from the end of a slice or array. +// DropRight drops n elements from the end of a slice. // Play: https://go.dev/play/p/GG0nXkSJJa3 func DropRight[T any, Slice ~[]T](collection Slice, n int) Slice { + if n < 0 { + panic("lo.DropRight: n must not be negative") + } + if len(collection) <= n { return Slice{} } @@ -451,7 +497,7 @@ func DropRight[T any, Slice ~[]T](collection Slice, n int) Slice { return append(result, collection[:len(collection)-n]...) } -// DropWhile drops elements from the beginning of a slice or array while the predicate returns true. +// DropWhile drops elements from the beginning of a slice while the predicate returns true. // Play: https://go.dev/play/p/7gBPYw2IK16 func DropWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice { i := 0 @@ -465,7 +511,7 @@ func DropWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) return append(result, collection[i:]...) } -// DropRightWhile drops elements from the end of a slice or array while the predicate returns true. +// DropRightWhile drops elements from the end of a slice while the predicate returns true. // Play: https://go.dev/play/p/3-n71oEC0Hz func DropRightWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice { i := len(collection) - 1 @@ -479,13 +525,13 @@ func DropRightWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) return append(result, collection[:i+1]...) } -// DropByIndex drops elements from a slice or array by the index. +// DropByIndex drops elements from a slice by the index. // A negative index will drop elements from the end of the slice. // Play: https://go.dev/play/p/bPIH4npZRxS -func DropByIndex[T any](collection []T, indexes ...int) []T { +func DropByIndex[T any, Slice ~[]T](collection Slice, indexes ...int) Slice { initialSize := len(collection) if initialSize == 0 { - return make([]T, 0) + return make(Slice, 0) } for i := range indexes { @@ -497,7 +543,7 @@ func DropByIndex[T any](collection []T, indexes ...int) []T { indexes = Uniq(indexes) sort.Ints(indexes) - result := make([]T, 0, initialSize) + result := make(Slice, 0, initialSize) result = append(result, collection...) for i := range indexes { @@ -511,8 +557,8 @@ func DropByIndex[T any](collection []T, indexes ...int) []T { return result } -// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return truthy for. -// Play: https://go.dev/play/p/YkLMODy1WEL +// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return true for. +// Play: https://go.dev/play/p/pFCF5WVB225 func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice { result := Slice{} @@ -525,11 +571,13 @@ func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index in return result } -// RejectMap is the opposite of FilterMap, this method returns a slice which obtained after both filtering and mapping using the given callback function. +// RejectMap is the opposite of FilterMap, this method returns a slice obtained after both filtering and mapping using the given callback function. // The callback function should return two values: // - the result of the mapping operation and // - whether the result element should be included or not. -func RejectMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R { +// +// Play: https://go.dev/play/p/W9Ug9r0QFkL +func RejectMap[T, R any](collection []T, callback func(item T, index int) (R, bool)) []R { result := []R{} for i := range collection { @@ -542,8 +590,9 @@ func RejectMap[T any, R any](collection []T, callback func(item T, index int) (R } // FilterReject mixes Filter and Reject, this method returns two slices, one for the elements of collection that -// predicate returns truthy for and one for the elements that predicate does not return truthy for. -func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept Slice, rejected Slice) { +// predicate returns true for and one for the elements that predicate does not return true for. +// Play: https://go.dev/play/p/lHSEGSznJjB +func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept, rejected Slice) { kept = make(Slice, 0, len(collection)) rejected = make(Slice, 0, len(collection)) @@ -558,9 +607,11 @@ func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bo return kept, rejected } -// Count counts the number of elements in the collection that compare equal to value. +// Count counts the number of elements in the collection that equal value. // Play: https://go.dev/play/p/Y3FlK54yveC -func Count[T comparable](collection []T, value T) (count int) { +func Count[T comparable](collection []T, value T) int { + var count int + for i := range collection { if collection[i] == value { count++ @@ -572,7 +623,9 @@ func Count[T comparable](collection []T, value T) (count int) { // CountBy counts the number of elements in the collection for which predicate is true. // Play: https://go.dev/play/p/ByQbNYQQi4X -func CountBy[T any](collection []T, predicate func(item T) bool) (count int) { +func CountBy[T any](collection []T, predicate func(item T) bool) int { + var count int + for i := range collection { if predicate(collection[i]) { count++ @@ -594,7 +647,7 @@ func CountValues[T comparable](collection []T) map[T]int { return result } -// CountValuesBy counts the number of each element return from mapper function. +// CountValuesBy counts the number of each element returned from mapper function. // Is equivalent to chaining lo.Map and lo.CountValues. // Play: https://go.dev/play/p/2U0dG1SnOmS func CountValuesBy[T any, U comparable](collection []T, mapper func(item T) U) map[U]int { @@ -632,7 +685,7 @@ func Subset[T any, Slice ~[]T](collection Slice, offset int, length uint) Slice // Slice returns a copy of a slice from `start` up to, but not including `end`. Like `slice[start:end]`, but does not panic on overflow. // Play: https://go.dev/play/p/8XWYhfMMA1h -func Slice[T any, Slice ~[]T](collection Slice, start int, end int) Slice { +func Slice[T any, Slice ~[]T](collection Slice, start, end int) Slice { size := len(collection) if start >= end { @@ -658,13 +711,13 @@ func Slice[T any, Slice ~[]T](collection Slice, start int, end int) Slice { // Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new. // Play: https://go.dev/play/p/XfPzmf9gql6 -func Replace[T comparable, Slice ~[]T](collection Slice, old T, new T, n int) Slice { +func Replace[T comparable, Slice ~[]T](collection Slice, old, nEw T, n int) Slice { result := make(Slice, len(collection)) copy(result, collection) for i := range result { if result[i] == old && n != 0 { - result[i] = new + result[i] = nEw n-- } } @@ -674,8 +727,8 @@ func Replace[T comparable, Slice ~[]T](collection Slice, old T, new T, n int) Sl // ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new. // Play: https://go.dev/play/p/a9xZFUHfYcV -func ReplaceAll[T comparable, Slice ~[]T](collection Slice, old T, new T) Slice { - return Replace(collection, old, new, -1) +func ReplaceAll[T comparable, Slice ~[]T](collection Slice, old, nEw T) Slice { + return Replace(collection, old, nEw, -1) } // Compact returns a slice of all non-zero elements. @@ -707,7 +760,6 @@ func IsSorted[T constraints.Ordered](collection []T) bool { } // IsSortedByKey checks if a slice is sorted by iteratee. -// Play: https://go.dev/play/p/wiG6XyBBu49 func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool { size := len(collection) @@ -728,18 +780,167 @@ func Splice[T any, Slice ~[]T](collection Slice, i int, elements ...T) Slice { sizeElements := len(elements) output := make(Slice, 0, sizeCollection+sizeElements) // preallocate memory for the output slice - if sizeElements == 0 { + switch { + case sizeElements == 0: return append(output, collection...) // simple copy - } else if i > sizeCollection { + case i > sizeCollection: // positive overflow return append(append(output, collection...), elements...) - } else if i < -sizeCollection { + case i < -sizeCollection: // negative overflow return append(append(output, elements...), collection...) - } else if i < 0 { + case i < 0: // backward i = sizeCollection + i } return append(append(append(output, collection[:i]...), elements...), collection[i:]...) } + +// Cut slices collection around the first instance of separator, returning the part of collection +// before and after separator. The found result reports whether separator appears in collection. +// If separator does not appear in s, cut returns collection, empty slice of []T, false. +// Play: https://go.dev/play/p/GiL3qhpIP3f +func Cut[T comparable, Slice ~[]T](collection, separator Slice) (before, after Slice, found bool) { + if len(separator) == 0 { + return make(Slice, 0), collection, true + } + + for i := 0; i+len(separator) <= len(collection); i++ { + match := true + for j := 0; j < len(separator); j++ { + if collection[i+j] != separator[j] { + match = false + break + } + } + if match { + return collection[:i], collection[i+len(separator):], true + } + } + + return collection, make(Slice, 0), false +} + +// CutPrefix returns collection without the provided leading prefix []T +// and reports whether it found the prefix. +// If s doesn't start with prefix, CutPrefix returns collection, false. +// If prefix is the empty []T, CutPrefix returns collection, true. +// Play: https://go.dev/play/p/7Plak4a1ICl +func CutPrefix[T comparable, Slice ~[]T](collection, separator Slice) (after Slice, found bool) { + if len(separator) == 0 { + return collection, true + } + if len(separator) > len(collection) { + return collection, false + } + + for i := range separator { + if collection[i] != separator[i] { + return collection, false + } + } + + return collection[len(separator):], true +} + +// CutSuffix returns collection without the provided ending suffix []T and reports +// whether it found the suffix. If s doesn't end with suffix, CutSuffix returns collection, false. +// If suffix is the empty []T, CutSuffix returns collection, true. +// Play: https://go.dev/play/p/7FKfBFvPTaT +func CutSuffix[T comparable, Slice ~[]T](collection, separator Slice) (before Slice, found bool) { + if len(separator) == 0 { + return collection, true + } + if len(separator) > len(collection) { + return collection, false + } + + start := len(collection) - len(separator) + for i := range separator { + if collection[start+i] != separator[i] { + return collection, false + } + } + + return collection[:start], true +} + +// Trim removes all the leading and trailing cutset from the collection. +// Play: https://go.dev/play/p/1an9mxLdRG5 +func Trim[T comparable, Slice ~[]T](collection, cutset Slice) Slice { + set := Keyify(cutset) + + i := 0 + for ; i < len(collection); i++ { + if _, ok := set[collection[i]]; !ok { + break + } + } + + if i >= len(collection) { + return Slice{} + } + + j := len(collection) - 1 + for ; j >= 0; j-- { + if _, ok := set[collection[j]]; !ok { + break + } + } + + result := make(Slice, 0, j+1-i) + return append(result, collection[i:j+1]...) +} + +// TrimLeft removes all the leading cutset from the collection. +// Play: https://go.dev/play/p/74aqfAYLmyi +func TrimLeft[T comparable, Slice ~[]T](collection, cutset Slice) Slice { + set := Keyify(cutset) + + return DropWhile(collection, func(item T) bool { + _, ok := set[item] + return ok + }) +} + +// TrimPrefix removes all the leading prefix from the collection. +// Play: https://go.dev/play/p/SHO6X-YegPg +func TrimPrefix[T comparable, Slice ~[]T](collection, prefix Slice) Slice { + if len(prefix) == 0 { + return collection + } + + for { + if !HasPrefix(collection, prefix) { + return collection + } + collection = collection[len(prefix):] + } +} + +// TrimRight removes all the trailing cutset from the collection. +// Play: https://go.dev/play/p/MRpAfR6sf0g +func TrimRight[T comparable, Slice ~[]T](collection, cutset Slice) Slice { + set := Keyify(cutset) + + return DropRightWhile(collection, func(item T) bool { + _, ok := set[item] + return ok + }) +} + +// TrimSuffix removes all the trailing suffix from the collection. +// Play: https://go.dev/play/p/IjEUrV0iofq +func TrimSuffix[T comparable, Slice ~[]T](collection, suffix Slice) Slice { + if len(suffix) == 0 { + return collection + } + + for { + if !HasSuffix(collection, suffix) { + return collection + } + collection = collection[:len(collection)-len(suffix)] + } +} diff --git a/vendor/github.com/samber/lo/string.go b/vendor/github.com/samber/lo/string.go index 923faa3..9590114 100644 --- a/vendor/github.com/samber/lo/string.go +++ b/vendor/github.com/samber/lo/string.go @@ -7,13 +7,14 @@ import ( "unicode" "unicode/utf8" - "github.com/samber/lo/internal/rand" + "github.com/samber/lo/internal/xrand" "golang.org/x/text/cases" "golang.org/x/text/language" ) var ( + //nolint:revive LowerCaseLettersCharset = []rune("abcdefghijklmnopqrstuvwxyz") UpperCaseLettersCharset = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") LettersCharset = append(LowerCaseLettersCharset, UpperCaseLettersCharset...) @@ -33,38 +34,49 @@ var ( // Play: https://go.dev/play/p/rRseOQVVum4 func RandomString(size int, charset []rune) string { if size <= 0 { - panic("lo.RandomString: Size parameter must be greater than 0") + panic("lo.RandomString: size must be greater than 0") } - if len(charset) <= 0 { - panic("lo.RandomString: Charset parameter must not be empty") + if len(charset) == 0 { + panic("lo.RandomString: charset must not be empty") } // see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go sb := strings.Builder{} sb.Grow(size) + + if len(charset) == 1 { + // Edge case, because if the charset is a single character, + // it will panic below (divide by zero). + // -> https://github.com/samber/lo/issues/679 + for i := 0; i < size; i++ { + sb.WriteRune(charset[0]) + } + return sb.String() + } + // Calculate the number of bits required to represent the charset, // e.g., for 62 characters, it would need 6 bits (since 62 -> 64 = 2^6) - letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset))))) + letterIDBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset))))) // Determine the corresponding bitmask, // e.g., for 62 characters, the bitmask would be 111111. - var letterIdMask int64 = 1<= 0; { + for i, cache, remain := size-1, xrand.Int64(), letterIDMax; i >= 0; { // Regenerate the random number if all available bits have been used if remain == 0 { - cache, remain = rand.Int64(), letterIdMax + cache, remain = xrand.Int64(), letterIDMax } // Select a character from the charset - if idx := int(cache & letterIdMask); idx < len(charset) { + if idx := int(cache & letterIDMask); idx < len(charset) { sb.WriteRune(charset[idx]) i-- } // Shift the bits to the right to prepare for the next character selection, // e.g., for 62 characters, shift by 6 bits. - cache >>= letterIdBits + cache >>= letterIDBits // Decrease the remaining number of uses for the current random number. remain-- } @@ -72,8 +84,8 @@ func RandomString(size int, charset []rune) string { } // nearestPowerOfTwo returns the nearest power of two. -func nearestPowerOfTwo(cap int) int { - n := cap - 1 +func nearestPowerOfTwo(capacity int) int { + n := capacity - 1 n |= n >> 1 n |= n >> 2 n |= n >> 4 @@ -112,12 +124,12 @@ func Substring[T ~string](str T, offset int, length uint) T { return T(strings.ReplaceAll(string(rs[offset:offset+int(length)]), "\x00", "")) } -// ChunkString returns an array of strings split into groups the length of size. If array can't be split evenly, -// the final chunk will be the remaining elements. +// ChunkString returns a slice of strings split into groups of length size. If the string can't be split evenly, +// the final chunk will be the remaining characters. // Play: https://go.dev/play/p/__FLTuJVz54 func ChunkString[T ~string](str T, size int) []T { if size <= 0 { - panic("lo.ChunkString: Size parameter must be greater than 0") + panic("lo.ChunkString: size must be greater than 0") } if len(str) == 0 { @@ -128,7 +140,7 @@ func ChunkString[T ~string](str T, size int) []T { return []T{str} } - var chunks = make([]T, 0, ((len(str)-1)/size)+1) + chunks := make([]T, 0, ((len(str)-1)/size)+1) currentLen := 0 currentStart := 0 for i := range str { @@ -150,6 +162,7 @@ func RuneLength(str string) int { } // PascalCase converts string to pascal case. +// Play: https://go.dev/play/p/Dy_V_6DUYhe func PascalCase(str string) string { items := Words(str) for i := range items { @@ -159,6 +172,7 @@ func PascalCase(str string) string { } // CamelCase converts string to camel case. +// Play: https://go.dev/play/p/Go6aKwUiq59 func CamelCase(str string) string { items := Words(str) for i, item := range items { @@ -172,6 +186,7 @@ func CamelCase(str string) string { } // KebabCase converts string to kebab case. +// Play: https://go.dev/play/p/96gT_WZnTVP func KebabCase(str string) string { items := Words(str) for i := range items { @@ -181,6 +196,7 @@ func KebabCase(str string) string { } // SnakeCase converts string to snake case. +// Play: https://go.dev/play/p/ziB0V89IeVH func SnakeCase(str string) string { items := Words(str) for i := range items { @@ -189,7 +205,8 @@ func SnakeCase(str string) string { return strings.Join(items, "_") } -// Words splits string into an array of its words. +// Words splits string into a slice of its words. +// Play: https://go.dev/play/p/-f3VIQqiaVw func Words(str string) []string { str = splitWordReg.ReplaceAllString(str, `$1$3$5$7 $2$4$6$8$9`) // example: Int8Value => Int 8Value => Int 8 Value @@ -206,6 +223,7 @@ func Words(str string) []string { } // Capitalize converts the first character of string to upper case and the remaining to lower case. +// Play: https://go.dev/play/p/uLTZZQXqnsa func Capitalize(str string) string { return cases.Title(language.English).String(str) } @@ -213,6 +231,7 @@ func Capitalize(str string) string { // Ellipsis trims and truncates a string to a specified length **in bytes** and appends an ellipsis // if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8), // truncating by byte length may split a character in the middle, potentially resulting in garbled output. +// Play: https://go.dev/play/p/qE93rgqe1TW func Ellipsis(str string, length int) string { str = strings.TrimSpace(str) diff --git a/vendor/github.com/samber/lo/time.go b/vendor/github.com/samber/lo/time.go index e98e80f..f295adb 100644 --- a/vendor/github.com/samber/lo/time.go +++ b/vendor/github.com/samber/lo/time.go @@ -1,13 +1,17 @@ package lo -import "time" +import ( + "time" +) // Duration returns the time taken to execute a function. +// Play: https://go.dev/play/p/HQfbBbAXaFP func Duration(cb func()) time.Duration { return Duration0(cb) } // Duration0 returns the time taken to execute a function. +// Play: https://go.dev/play/p/HQfbBbAXaFP func Duration0(cb func()) time.Duration { start := time.Now() cb() @@ -15,6 +19,7 @@ func Duration0(cb func()) time.Duration { } // Duration1 returns the time taken to execute a function. +// Play: https://go.dev/play/p/HQfbBbAXaFP func Duration1[A any](cb func() A) (A, time.Duration) { start := time.Now() a := cb() @@ -22,6 +27,7 @@ func Duration1[A any](cb func() A) (A, time.Duration) { } // Duration2 returns the time taken to execute a function. +// Play: https://go.dev/play/p/HQfbBbAXaFP func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) { start := time.Now() a, b := cb() @@ -29,6 +35,7 @@ func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) { } // Duration3 returns the time taken to execute a function. +// Play: https://go.dev/play/p/xr863iwkAxQ func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) { start := time.Now() a, b, c := cb() @@ -36,6 +43,7 @@ func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) { } // Duration4 returns the time taken to execute a function. +// Play: https://go.dev/play/p/xr863iwkAxQ func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duration) { start := time.Now() a, b, c, d := cb() @@ -43,6 +51,7 @@ func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duratio } // Duration5 returns the time taken to execute a function. +// Play: https://go.dev/play/p/xr863iwkAxQ func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, time.Duration) { start := time.Now() a, b, c, d, e := cb() @@ -50,6 +59,7 @@ func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, tim } // Duration6 returns the time taken to execute a function. +// Play: https://go.dev/play/p/mR4bTQKO-Tf func Duration6[A, B, C, D, E, F any](cb func() (A, B, C, D, E, F)) (A, B, C, D, E, F, time.Duration) { start := time.Now() a, b, c, d, e, f := cb() @@ -57,6 +67,7 @@ func Duration6[A, B, C, D, E, F any](cb func() (A, B, C, D, E, F)) (A, B, C, D, } // Duration7 returns the time taken to execute a function. +// Play: https://go.dev/play/p/jgIAcBWWInS func Duration7[A, B, C, D, E, F, G any](cb func() (A, B, C, D, E, F, G)) (A, B, C, D, E, F, G, time.Duration) { start := time.Now() a, b, c, d, e, f, g := cb() @@ -64,6 +75,7 @@ func Duration7[A, B, C, D, E, F, G any](cb func() (A, B, C, D, E, F, G)) (A, B, } // Duration8 returns the time taken to execute a function. +// Play: https://go.dev/play/p/T8kxpG1c5Na func Duration8[A, B, C, D, E, F, G, H any](cb func() (A, B, C, D, E, F, G, H)) (A, B, C, D, E, F, G, H, time.Duration) { start := time.Now() a, b, c, d, e, f, g, h := cb() @@ -71,6 +83,7 @@ func Duration8[A, B, C, D, E, F, G, H any](cb func() (A, B, C, D, E, F, G, H)) ( } // Duration9 returns the time taken to execute a function. +// Play: https://go.dev/play/p/bg9ix2VrZ0j func Duration9[A, B, C, D, E, F, G, H, I any](cb func() (A, B, C, D, E, F, G, H, I)) (A, B, C, D, E, F, G, H, I, time.Duration) { start := time.Now() a, b, c, d, e, f, g, h, i := cb() @@ -78,6 +91,7 @@ func Duration9[A, B, C, D, E, F, G, H, I any](cb func() (A, B, C, D, E, F, G, H, } // Duration10 returns the time taken to execute a function. +// Play: https://go.dev/play/p/Y3n7oJXqJbk func Duration10[A, B, C, D, E, F, G, H, I, J any](cb func() (A, B, C, D, E, F, G, H, I, J)) (A, B, C, D, E, F, G, H, I, J, time.Duration) { start := time.Now() a, b, c, d, e, f, g, h, i, j := cb() diff --git a/vendor/github.com/samber/lo/tuples.go b/vendor/github.com/samber/lo/tuples.go index e355d0c..d9805b2 100644 --- a/vendor/github.com/samber/lo/tuples.go +++ b/vendor/github.com/samber/lo/tuples.go @@ -48,57 +48,57 @@ func T9[A, B, C, D, E, F, G, H, I any](a A, b B, c C, d D, e E, f F, g G, h H, i return Tuple9[A, B, C, D, E, F, G, H, I]{A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i} } -// Unpack2 returns values contained in tuple. +// Unpack2 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack2[A, B any](tuple Tuple2[A, B]) (A, B) { return tuple.A, tuple.B } -// Unpack3 returns values contained in tuple. +// Unpack3 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack3[A, B, C any](tuple Tuple3[A, B, C]) (A, B, C) { return tuple.A, tuple.B, tuple.C } -// Unpack4 returns values contained in tuple. +// Unpack4 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack4[A, B, C, D any](tuple Tuple4[A, B, C, D]) (A, B, C, D) { return tuple.A, tuple.B, tuple.C, tuple.D } -// Unpack5 returns values contained in tuple. +// Unpack5 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack5[A, B, C, D, E any](tuple Tuple5[A, B, C, D, E]) (A, B, C, D, E) { return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E } -// Unpack6 returns values contained in tuple. +// Unpack6 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack6[A, B, C, D, E, F any](tuple Tuple6[A, B, C, D, E, F]) (A, B, C, D, E, F) { return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F } -// Unpack7 returns values contained in tuple. +// Unpack7 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack7[A, B, C, D, E, F, G any](tuple Tuple7[A, B, C, D, E, F, G]) (A, B, C, D, E, F, G) { return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G } -// Unpack8 returns values contained in tuple. +// Unpack8 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack8[A, B, C, D, E, F, G, H any](tuple Tuple8[A, B, C, D, E, F, G, H]) (A, B, C, D, E, F, G, H) { return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G, tuple.H } -// Unpack9 returns values contained in tuple. +// Unpack9 returns values contained in a tuple. // Play: https://go.dev/play/p/xVP_k0kJ96W func Unpack9[A, B, C, D, E, F, G, H, I any](tuple Tuple9[A, B, C, D, E, F, G, H, I]) (A, B, C, D, E, F, G, H, I) { return tuple.A, tuple.B, tuple.C, tuple.D, tuple.E, tuple.F, tuple.G, tuple.H, tuple.I } // Zip2 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip2[A, B any](a []A, b []B) []Tuple2[A, B] { size := Max([]int{len(a), len(b)}) @@ -119,8 +119,8 @@ func Zip2[A, B any](a []A, b []B) []Tuple2[A, B] { } // Zip3 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip3[A, B, C any](a []A, b []B, c []C) []Tuple3[A, B, C] { size := Max([]int{len(a), len(b), len(c)}) @@ -143,8 +143,8 @@ func Zip3[A, B, C any](a []A, b []B, c []C) []Tuple3[A, B, C] { } // Zip4 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip4[A, B, C, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] { size := Max([]int{len(a), len(b), len(c), len(d)}) @@ -169,8 +169,8 @@ func Zip4[A, B, C, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] { } // Zip5 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip5[A, B, C, D, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] { size := Max([]int{len(a), len(b), len(c), len(d), len(e)}) @@ -197,8 +197,8 @@ func Zip5[A, B, C, D, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C } // Zip6 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip6[A, B, C, D, E, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}) @@ -227,8 +227,8 @@ func Zip6[A, B, C, D, E, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tupl } // Zip7 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip7[A, B, C, D, E, F, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}) @@ -259,8 +259,8 @@ func Zip7[A, B, C, D, E, F, G any](a []A, b []B, c []C, d []D, e []E, f []F, g [ } // Zip8 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip8[A, B, C, D, E, F, G, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)}) @@ -293,8 +293,8 @@ func Zip8[A, B, C, D, E, F, G, H any](a []A, b []B, c []C, d []D, e []E, f []F, } // Zip9 creates a slice of grouped elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. // Play: https://go.dev/play/p/jujaA6GaJTp func Zip9[A, B, C, D, E, F, G, H, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)}) @@ -329,9 +329,10 @@ func Zip9[A, B, C, D, E, F, G, H, I any](a []A, b []B, c []C, d []D, e []E, f [] } // ZipBy2 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy2[A any, B any, Out any](a []A, b []B, iteratee func(a A, b B) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/wlHur6yO8rR +func ZipBy2[A, B, Out any](a []A, b []B, iteratee func(a A, b B) Out) []Out { size := Max([]int{len(a), len(b)}) result := make([]Out, 0, size) @@ -347,9 +348,10 @@ func ZipBy2[A any, B any, Out any](a []A, b []B, iteratee func(a A, b B) Out) [] } // ZipBy3 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy3[A any, B any, C any, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/j9maveOnSQX +func ZipBy3[A, B, C, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C) Out) []Out { size := Max([]int{len(a), len(b), len(c)}) result := make([]Out, 0, size) @@ -366,9 +368,10 @@ func ZipBy3[A any, B any, C any, Out any](a []A, b []B, c []C, iteratee func(a A } // ZipBy4 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy4[A any, B any, C any, D any, Out any](a []A, b []B, c []C, d []D, iteratee func(a A, b B, c C, d D) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/Y1eF2Ke0Ayz +func ZipBy4[A, B, C, D, Out any](a []A, b []B, c []C, d []D, iteratee func(a A, b B, c C, d D) Out) []Out { size := Max([]int{len(a), len(b), len(c), len(d)}) result := make([]Out, 0, size) @@ -386,9 +389,10 @@ func ZipBy4[A any, B any, C any, D any, Out any](a []A, b []B, c []C, d []D, ite } // ZipBy5 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy5[A any, B any, C any, D any, E any, Out any](a []A, b []B, c []C, d []D, e []E, iteratee func(a A, b B, c C, d D, e E) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/SLynyalh5Oa +func ZipBy5[A, B, C, D, E, Out any](a []A, b []B, c []C, d []D, e []E, iteratee func(a A, b B, c C, d D, e E) Out) []Out { size := Max([]int{len(a), len(b), len(c), len(d), len(e)}) result := make([]Out, 0, size) @@ -407,9 +411,10 @@ func ZipBy5[A any, B any, C any, D any, E any, Out any](a []A, b []B, c []C, d [ } // ZipBy6 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy6[A any, B any, C any, D any, E any, F any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, iteratee func(a A, b B, c C, d D, e E, f F) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/IK6KVgw9e-S +func ZipBy6[A, B, C, D, E, F, Out any](a []A, b []B, c []C, d []D, e []E, f []F, iteratee func(a A, b B, c C, d D, e E, f F) Out) []Out { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}) result := make([]Out, 0, size) @@ -429,10 +434,11 @@ func ZipBy6[A any, B any, C any, D any, E any, F any, Out any](a []A, b []B, c [ } // ZipBy7 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy7[A any, B any, C any, D any, E any, F any, G any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, iteratee func(a A, b B, c C, d D, e E, f F, g G) Out) []Out { - size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}) +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/4uW6a2vXh8w +func ZipBy7[A, B, C, D, E, F, G, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, iteratee func(a A, b B, c C, d D, e E, f F, g G) Out) []Out { + size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}) result := make([]Out, 0, size) @@ -452,10 +458,11 @@ func ZipBy7[A any, B any, C any, D any, E any, F any, G any, Out any](a []A, b [ } // ZipBy8 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy8[A any, B any, C any, D any, E any, F any, G any, H any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out { - size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}) +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/tk8xW7XzY4v +func ZipBy8[A, B, C, D, E, F, G, H, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out { + size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)}) result := make([]Out, 0, size) @@ -476,9 +483,10 @@ func ZipBy8[A any, B any, C any, D any, E any, F any, G any, H any, Out any](a [ } // ZipBy9 creates a slice of transformed elements, the first of which contains the first elements -// of the given arrays, the second of which contains the second elements of the given arrays, and so on. -// When collections have different size, the Tuple attributes are filled with zero value. -func ZipBy9[A any, B any, C any, D any, E any, F any, G any, H any, I any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out { +// of the given slices, the second of which contains the second elements of the given slices, and so on. +// When collections are different sizes, the Tuple attributes are filled with zero value. +// Play: https://go.dev/play/p/VGqjDmQ9YqX +func ZipBy9[A, B, C, D, E, F, G, H, I, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out { size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)}) result := make([]Out, 0, size) @@ -500,7 +508,7 @@ func ZipBy9[A any, B any, C any, D any, E any, F any, G any, H any, I any, Out a return result } -// Unzip2 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip2 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip2[A, B any](tuples []Tuple2[A, B]) ([]A, []B) { @@ -516,7 +524,7 @@ func Unzip2[A, B any](tuples []Tuple2[A, B]) ([]A, []B) { return r1, r2 } -// Unzip3 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip3 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip3[A, B, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) { @@ -534,7 +542,7 @@ func Unzip3[A, B, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) { return r1, r2, r3 } -// Unzip4 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip4 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip4[A, B, C, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) { @@ -554,7 +562,7 @@ func Unzip4[A, B, C, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) { return r1, r2, r3, r4 } -// Unzip5 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip5 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip5[A, B, C, D, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) { @@ -576,7 +584,7 @@ func Unzip5[A, B, C, D, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, [ return r1, r2, r3, r4, r5 } -// Unzip6 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip6 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip6[A, B, C, D, E, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) { @@ -600,7 +608,7 @@ func Unzip6[A, B, C, D, E, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, return r1, r2, r3, r4, r5, r6 } -// Unzip7 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip7 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip7[A, B, C, D, E, F, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) { @@ -626,7 +634,7 @@ func Unzip7[A, B, C, D, E, F, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, return r1, r2, r3, r4, r5, r6, r7 } -// Unzip8 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip8 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip8[A, B, C, D, E, F, G, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) { @@ -654,7 +662,7 @@ func Unzip8[A, B, C, D, E, F, G, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) return r1, r2, r3, r4, r5, r6, r7, r8 } -// Unzip9 accepts an array of grouped elements and creates an array regrouping the elements +// Unzip9 accepts a slice of grouped elements and creates a slice regrouping the elements // to their pre-zip configuration. // Play: https://go.dev/play/p/ciHugugvaAW func Unzip9[A, B, C, D, E, F, G, H, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) { @@ -684,9 +692,10 @@ func Unzip9[A, B, C, D, E, F, G, H, I any](tuples []Tuple9[A, B, C, D, E, F, G, return r1, r2, r3, r4, r5, r6, r7, r8, r9 } -// UnzipBy2 iterates over a collection and creates an array regrouping the elements +// UnzipBy2 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy2[In any, A any, B any](items []In, iteratee func(In) (a A, b B)) ([]A, []B) { +// Play: https://go.dev/play/p/tN8yqaRZz0r +func UnzipBy2[In, A, B any](items []In, iteratee func(In) (a A, b B)) ([]A, []B) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -700,9 +709,10 @@ func UnzipBy2[In any, A any, B any](items []In, iteratee func(In) (a A, b B)) ([ return r1, r2 } -// UnzipBy3 iterates over a collection and creates an array regrouping the elements +// UnzipBy3 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy3[In any, A any, B any, C any](items []In, iteratee func(In) (a A, b B, c C)) ([]A, []B, []C) { +// Play: https://go.dev/play/p/36ITO2DlQq1 +func UnzipBy3[In, A, B, C any](items []In, iteratee func(In) (a A, b B, c C)) ([]A, []B, []C) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -718,9 +728,10 @@ func UnzipBy3[In any, A any, B any, C any](items []In, iteratee func(In) (a A, b return r1, r2, r3 } -// UnzipBy4 iterates over a collection and creates an array regrouping the elements +// UnzipBy4 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy4[In any, A any, B any, C any, D any](items []In, iteratee func(In) (a A, b B, c C, d D)) ([]A, []B, []C, []D) { +// Play: https://go.dev/play/p/zJ6qY1dD1rL +func UnzipBy4[In, A, B, C, D any](items []In, iteratee func(In) (a A, b B, c C, d D)) ([]A, []B, []C, []D) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -738,9 +749,10 @@ func UnzipBy4[In any, A any, B any, C any, D any](items []In, iteratee func(In) return r1, r2, r3, r4 } -// UnzipBy5 iterates over a collection and creates an array regrouping the elements +// UnzipBy5 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy5[In any, A any, B any, C any, D any, E any](items []In, iteratee func(In) (a A, b B, c C, d D, e E)) ([]A, []B, []C, []D, []E) { +// Play: https://go.dev/play/p/3f7jKkV9xZt +func UnzipBy5[In, A, B, C, D, E any](items []In, iteratee func(In) (a A, b B, c C, d D, e E)) ([]A, []B, []C, []D, []E) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -760,9 +772,10 @@ func UnzipBy5[In any, A any, B any, C any, D any, E any](items []In, iteratee fu return r1, r2, r3, r4, r5 } -// UnzipBy6 iterates over a collection and creates an array regrouping the elements +// UnzipBy6 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy6[In any, A any, B any, C any, D any, E any, F any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F)) ([]A, []B, []C, []D, []E, []F) { +// Play: https://go.dev/play/p/8Y1b7tKu2pL +func UnzipBy6[In, A, B, C, D, E, F any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F)) ([]A, []B, []C, []D, []E, []F) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -784,9 +797,10 @@ func UnzipBy6[In any, A any, B any, C any, D any, E any, F any](items []In, iter return r1, r2, r3, r4, r5, r6 } -// UnzipBy7 iterates over a collection and creates an array regrouping the elements +// UnzipBy7 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy7[In any, A any, B any, C any, D any, E any, F any, G any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G)) ([]A, []B, []C, []D, []E, []F, []G) { +// Play: https://go.dev/play/p/7j1kLmVn3pM +func UnzipBy7[In, A, B, C, D, E, F, G any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G)) ([]A, []B, []C, []D, []E, []F, []G) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -810,9 +824,10 @@ func UnzipBy7[In any, A any, B any, C any, D any, E any, F any, G any](items []I return r1, r2, r3, r4, r5, r6, r7 } -// UnzipBy8 iterates over a collection and creates an array regrouping the elements +// UnzipBy8 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy8[In any, A any, B any, C any, D any, E any, F any, G any, H any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H)) ([]A, []B, []C, []D, []E, []F, []G, []H) { +// Play: https://go.dev/play/p/1n2k3L4m5N6 +func UnzipBy8[In, A, B, C, D, E, F, G, H any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H)) ([]A, []B, []C, []D, []E, []F, []G, []H) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -838,9 +853,10 @@ func UnzipBy8[In any, A any, B any, C any, D any, E any, F any, G any, H any](it return r1, r2, r3, r4, r5, r6, r7, r8 } -// UnzipBy9 iterates over a collection and creates an array regrouping the elements +// UnzipBy9 iterates over a collection and creates a slice regrouping the elements // to their pre-zip configuration. -func UnzipBy9[In any, A any, B any, C any, D any, E any, F any, G any, H any, I any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H, i I)) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) { +// Play: https://go.dev/play/p/7o8p9q0r1s2 +func UnzipBy9[In, A, B, C, D, E, F, G, H, I any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H, i I)) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) { size := len(items) r1 := make([]A, 0, size) r2 := make([]B, 0, size) @@ -868,66 +884,75 @@ func UnzipBy9[In any, A any, B any, C any, D any, E any, F any, G any, H any, I return r1, r2, r3, r4, r5, r6, r7, r8, r9 } -// CrossJoin2 combines every items from one list with every items from others. +// CrossJoin2 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/3VFppyL9FDU func CrossJoin2[A, B any](listA []A, listB []B) []Tuple2[A, B] { return CrossJoinBy2(listA, listB, T2[A, B]) } -// CrossJoin3 combines every items from one list with every items from others. +// CrossJoin3 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/2WGeHyJj4fK func CrossJoin3[A, B, C any](listA []A, listB []B, listC []C) []Tuple3[A, B, C] { return CrossJoinBy3(listA, listB, listC, T3[A, B, C]) } -// CrossJoin4 combines every items from one list with every items from others. +// CrossJoin4 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/6XhKjLmMnNp func CrossJoin4[A, B, C, D any](listA []A, listB []B, listC []C, listD []D) []Tuple4[A, B, C, D] { return CrossJoinBy4(listA, listB, listC, listD, T4[A, B, C, D]) } -// CrossJoin5 combines every items from one list with every items from others. +// CrossJoin5 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/7oPqRsTuVwX func CrossJoin5[A, B, C, D, E any](listA []A, listB []B, listC []C, listD []D, listE []E) []Tuple5[A, B, C, D, E] { return CrossJoinBy5(listA, listB, listC, listD, listE, T5[A, B, C, D, E]) } -// CrossJoin6 combines every items from one list with every items from others. +// CrossJoin6 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/8yZ1aB2cD3e func CrossJoin6[A, B, C, D, E, F any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F) []Tuple6[A, B, C, D, E, F] { return CrossJoinBy6(listA, listB, listC, listD, listE, listF, T6[A, B, C, D, E, F]) } -// CrossJoin7 combines every items from one list with every items from others. +// CrossJoin7 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/9f4g5h6i7j8 func CrossJoin7[A, B, C, D, E, F, G any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G) []Tuple7[A, B, C, D, E, F, G] { return CrossJoinBy7(listA, listB, listC, listD, listE, listF, listG, T7[A, B, C, D, E, F, G]) } -// CrossJoin8 combines every items from one list with every items from others. +// CrossJoin8 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/0k1l2m3n4o5 func CrossJoin8[A, B, C, D, E, F, G, H any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H) []Tuple8[A, B, C, D, E, F, G, H] { return CrossJoinBy8(listA, listB, listC, listD, listE, listF, listG, listH, T8[A, B, C, D, E, F, G, H]) } -// CrossJoin9 combines every items from one list with every items from others. +// CrossJoin9 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/6p7q8r9s0t1 func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I) []Tuple9[A, B, C, D, E, F, G, H, I] { return CrossJoinBy9(listA, listB, listC, listD, listE, listF, listG, listH, listI, T9[A, B, C, D, E, F, G, H, I]) } -// CrossJoinBy2 combines every items from one list with every items from others. +// CrossJoinBy2 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/8Y7btpvuA-C func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Out) []Out { size := len(listA) * len(listB) if size == 0 { @@ -945,10 +970,11 @@ func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Ou return result } -// CrossJoinBy3 combines every items from one list with every items from others. +// CrossJoinBy3 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/3z4y5x6w7v8 func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project func(a A, b B, c C) Out) []Out { size := len(listA) * len(listB) * len(listC) if size == 0 { @@ -968,10 +994,11 @@ func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project fun return result } -// CrossJoinBy4 combines every items from one list with every items from others. +// CrossJoinBy4 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/8b9c0d1e2f3 func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, project func(a A, b B, c C, d D) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) if size == 0 { @@ -993,10 +1020,11 @@ func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD [] return result } -// CrossJoinBy5 combines every items from one list with every items from others. +// CrossJoinBy5 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/4g5h6i7j8k9 func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, project func(a A, b B, c C, d D, e E) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) if size == 0 { @@ -1020,10 +1048,11 @@ func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD return result } -// CrossJoinBy6 combines every items from one list with every items from others. +// CrossJoinBy6 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/1l2m3n4o5p6 func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, project func(a A, b B, c C, d D, e E, f F) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) if size == 0 { @@ -1049,10 +1078,11 @@ func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, li return result } -// CrossJoinBy7 combines every items from one list with every items from others. +// CrossJoinBy7 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/7q8r9s0t1u2 func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, project func(a A, b B, c C, d D, e E, f F, g G) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) if size == 0 { @@ -1080,10 +1110,11 @@ func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, return result } -// CrossJoinBy8 combines every items from one list with every items from others. +// CrossJoinBy8 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/3v4w5x6y7z8 func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, project func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) if size == 0 { @@ -1113,10 +1144,11 @@ func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC [ return result } -// CrossJoinBy9 combines every items from one list with every items from others. +// CrossJoinBy9 combines every item from one list with every item from others. // It is the cartesian product of lists received as arguments. The project function // is used to create the output values. -// It returns an empty list if a list is empty. +// Returns an empty list if a list is empty. +// Play: https://go.dev/play/p/9a0b1c2d3e4 func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, project func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out { size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) * len(listI) if size == 0 { diff --git a/vendor/github.com/samber/lo/type_manipulation.go b/vendor/github.com/samber/lo/type_manipulation.go index bcf990c..281f316 100644 --- a/vendor/github.com/samber/lo/type_manipulation.go +++ b/vendor/github.com/samber/lo/type_manipulation.go @@ -3,28 +3,41 @@ package lo import "reflect" // IsNil checks if a value is nil or if it's a reference type with a nil underlying value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func IsNil(x any) bool { - defer func() { recover() }() // nolint:errcheck - return x == nil || reflect.ValueOf(x).IsNil() + if x == nil { + return true + } + v := reflect.ValueOf(x) + switch v.Kind() { //nolint:exhaustive + case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice: + return v.IsNil() + default: + return false + } } // IsNotNil checks if a value is not nil or if it's not a reference type with a nil underlying value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func IsNotNil(x any) bool { return !IsNil(x) } // ToPtr returns a pointer copy of value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func ToPtr[T any](x T) *T { return &x } // Nil returns a nil pointer of type. +// Play: https://go.dev/play/p/P2sD0PMXw4F func Nil[T any]() *T { return nil } // EmptyableToPtr returns a pointer copy of value if it's nonzero. // Otherwise, returns nil pointer. +// Play: https://go.dev/play/p/P2sD0PMXw4F func EmptyableToPtr[T any](x T) *T { // 🤮 isZero := reflect.ValueOf(&x).Elem().IsZero() @@ -36,6 +49,7 @@ func EmptyableToPtr[T any](x T) *T { } // FromPtr returns the pointer value or empty. +// Play: https://go.dev/play/p/mhD9CwO3X0m func FromPtr[T any](x *T) T { if x == nil { return Empty[T]() @@ -45,6 +59,7 @@ func FromPtr[T any](x *T) T { } // FromPtrOr returns the pointer value or the fallback value. +// Play: https://go.dev/play/p/mhD9CwO3X0m func FromPtrOr[T any](x *T, fallback T) T { if x == nil { return fallback @@ -53,7 +68,8 @@ func FromPtrOr[T any](x *T, fallback T) T { return *x } -// ToSlicePtr returns a slice of pointer copy of value. +// ToSlicePtr returns a slice of pointers to each value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func ToSlicePtr[T any](collection []T) []*T { result := make([]*T, len(collection)) @@ -65,6 +81,7 @@ func ToSlicePtr[T any](collection []T) []*T { // FromSlicePtr returns a slice with the pointer values. // Returns a zero value in case of a nil pointer element. +// Play: https://go.dev/play/p/lbunFvzlUDX func FromSlicePtr[T any](collection []*T) []T { return Map(collection, func(x *T, _ int) T { if x == nil { @@ -85,7 +102,8 @@ func FromSlicePtrOr[T any](collection []*T, fallback T) []T { }) } -// ToAnySlice returns a slice with all elements mapped to `any` type +// ToAnySlice returns a slice with all elements mapped to `any` type. +// Play: https://go.dev/play/p/P2sD0PMXw4F func ToAnySlice[T any](collection []T) []any { result := make([]any, len(collection)) for i := range collection { @@ -94,61 +112,65 @@ func ToAnySlice[T any](collection []T) []any { return result } -// FromAnySlice returns an `any` slice with all elements mapped to a type. +// FromAnySlice returns a slice with all elements mapped to a type. // Returns false in case of type conversion failure. -func FromAnySlice[T any](in []any) (out []T, ok bool) { - defer func() { - if r := recover(); r != nil { - out = []T{} - ok = false - } - }() - - result := make([]T, len(in)) +// Play: https://go.dev/play/p/P2sD0PMXw4F +func FromAnySlice[T any](in []any) ([]T, bool) { + out := make([]T, len(in)) for i := range in { - result[i] = in[i].(T) + t, ok := in[i].(T) + if !ok { + return []T{}, false + } + out[i] = t } - return result, true + return out, true } // Empty returns the zero value (https://go.dev/ref/spec#The_zero_value). +// Play: https://go.dev/play/p/P2sD0PMXw4F func Empty[T any]() T { var zero T return zero } // IsEmpty returns true if argument is a zero value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func IsEmpty[T comparable](v T) bool { var zero T return zero == v } // IsNotEmpty returns true if argument is not a zero value. +// Play: https://go.dev/play/p/P2sD0PMXw4F func IsNotEmpty[T comparable](v T) bool { var zero T return zero != v } // Coalesce returns the first non-empty arguments. Arguments must be comparable. -func Coalesce[T comparable](values ...T) (result T, ok bool) { +// Play: https://go.dev/play/p/Gyo9otyvFHH +func Coalesce[T comparable](values ...T) (T, bool) { + var zero T + for i := range values { - if values[i] != result { - result = values[i] - ok = true - return + if values[i] != zero { + return values[i], true } } - return + return zero, false } // CoalesceOrEmpty returns the first non-empty arguments. Arguments must be comparable. +// Play: https://go.dev/play/p/Gyo9otyvFHH func CoalesceOrEmpty[T comparable](v ...T) T { result, _ := Coalesce(v...) return result } // CoalesceSlice returns the first non-zero slice. +// Play: https://go.dev/play/p/Gyo9otyvFHH func CoalesceSlice[T any](v ...[]T) ([]T, bool) { for i := range v { if v[i] != nil && len(v[i]) > 0 { @@ -159,6 +181,7 @@ func CoalesceSlice[T any](v ...[]T) ([]T, bool) { } // CoalesceSliceOrEmpty returns the first non-zero slice. +// Play: https://go.dev/play/p/Gyo9otyvFHH func CoalesceSliceOrEmpty[T any](v ...[]T) []T { for i := range v { if v[i] != nil && len(v[i]) > 0 { @@ -169,6 +192,7 @@ func CoalesceSliceOrEmpty[T any](v ...[]T) []T { } // CoalesceMap returns the first non-zero map. +// Play: https://go.dev/play/p/Gyo9otyvFHH func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) { for i := range v { if v[i] != nil && len(v[i]) > 0 { @@ -179,6 +203,7 @@ func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) { } // CoalesceMapOrEmpty returns the first non-zero map. +// Play: https://go.dev/play/p/Gyo9otyvFHH func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V { for i := range v { if v[i] != nil && len(v[i]) > 0 { diff --git a/vendor/github.com/samber/lo/types.go b/vendor/github.com/samber/lo/types.go index 1c6f0d0..ccd3d07 100644 --- a/vendor/github.com/samber/lo/types.go +++ b/vendor/github.com/samber/lo/types.go @@ -12,7 +12,8 @@ type Tuple2[A, B any] struct { B B } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/yrtn7QJTmL_E func (t Tuple2[A, B]) Unpack() (A, B) { return t.A, t.B } @@ -24,7 +25,8 @@ type Tuple3[A, B, C any] struct { C C } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/yrtn7QJTmL_E func (t Tuple3[A, B, C]) Unpack() (A, B, C) { return t.A, t.B, t.C } @@ -37,7 +39,8 @@ type Tuple4[A, B, C, D any] struct { D D } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/yrtn7QJTmL_E func (t Tuple4[A, B, C, D]) Unpack() (A, B, C, D) { return t.A, t.B, t.C, t.D } @@ -51,7 +54,8 @@ type Tuple5[A, B, C, D, E any] struct { E E } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/7J4KrtgtK3M func (t Tuple5[A, B, C, D, E]) Unpack() (A, B, C, D, E) { return t.A, t.B, t.C, t.D, t.E } @@ -66,7 +70,8 @@ type Tuple6[A, B, C, D, E, F any] struct { F F } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/7J4KrtgtK3M func (t Tuple6[A, B, C, D, E, F]) Unpack() (A, B, C, D, E, F) { return t.A, t.B, t.C, t.D, t.E, t.F } @@ -82,7 +87,8 @@ type Tuple7[A, B, C, D, E, F, G any] struct { G G } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/Ow9Zgf_zeiA func (t Tuple7[A, B, C, D, E, F, G]) Unpack() (A, B, C, D, E, F, G) { return t.A, t.B, t.C, t.D, t.E, t.F, t.G } @@ -99,7 +105,8 @@ type Tuple8[A, B, C, D, E, F, G, H any] struct { H H } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/Ow9Zgf_zeiA func (t Tuple8[A, B, C, D, E, F, G, H]) Unpack() (A, B, C, D, E, F, G, H) { return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H } @@ -117,7 +124,8 @@ type Tuple9[A, B, C, D, E, F, G, H, I any] struct { I I } -// Unpack returns values contained in tuple. +// Unpack returns values contained in a tuple. +// Play: https://go.dev/play/p/Ow9Zgf_zeiA func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unpack() (A, B, C, D, E, F, G, H, I) { return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H, t.I } diff --git a/vendor/modules.txt b/vendor/modules.txt index fd9348b..146c0c9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -14,25 +14,26 @@ github.com/google/go-cmp/cmp/internal/value # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/samber/lo v1.51.0 +# github.com/samber/lo v1.52.0 ## explicit; go 1.18 github.com/samber/lo github.com/samber/lo/internal/constraints -github.com/samber/lo/internal/rand +github.com/samber/lo/internal/xrand +github.com/samber/lo/internal/xtime github.com/samber/lo/mutable # github.com/stretchr/testify v1.11.1 ## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml github.com/stretchr/testify/require -# golang.org/x/crypto v0.41.0 -## explicit; go 1.23.0 +# golang.org/x/crypto v0.43.0 +## explicit; go 1.24.0 golang.org/x/crypto/blake2b -# golang.org/x/sys v0.35.0 -## explicit; go 1.23.0 +# golang.org/x/sys v0.37.0 +## explicit; go 1.24.0 golang.org/x/sys/cpu -# golang.org/x/text v0.28.0 -## explicit; go 1.23.0 +# golang.org/x/text v0.30.0 +## explicit; go 1.24.0 golang.org/x/text/cases golang.org/x/text/encoding golang.org/x/text/encoding/charmap