complete RestMux
This commit is contained in:
parent
8107b7d567
commit
7127751a75
|
@ -8,3 +8,10 @@
|
||||||
- 支持静态文件
|
- 支持静态文件
|
||||||
- Proxy
|
- Proxy
|
||||||
- 支持rewrite
|
- 支持rewrite
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
RestMux 提供Restful API注册功能的 ServerMux
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -2,7 +2,8 @@ package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
"git.pyer.club/kingecg/gohttpd/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
|
@ -22,36 +23,12 @@ var AdminRoutes = []Route{
|
||||||
{"GET", "/about", about},
|
{"GET", "/about", about},
|
||||||
}
|
}
|
||||||
|
|
||||||
type PServeMux struct {
|
var AdminServerMux *server.RestMux
|
||||||
// routes map[string]map[string]http.HandlerFunc
|
|
||||||
*http.ServeMux
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PServeMux) Use(route Route) {
|
|
||||||
method := strings.ToLower(route.Method)
|
|
||||||
p.ServeMux.HandleFunc("/"+method+route.Path, route.Handle)
|
|
||||||
// _, exist := p.routes[route.Path]
|
|
||||||
// if !exist {
|
|
||||||
// mroutes := make(map[string]http.HandlerFunc)
|
|
||||||
// p.routes[route.Path] = mroutes
|
|
||||||
// p.ServeMux.HandleFunc(route.Path, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// p.routes[route.Path][r.Method](w, r)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// p.routes[route.Path][route.Method] = route.Handle
|
|
||||||
}
|
|
||||||
func (p *PServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
r.URL.Path = "/" + strings.ToLower(r.Method) + r.URL.Path
|
|
||||||
r.RequestURI = "/" + strings.ToLower(r.Method) + r.RequestURI
|
|
||||||
p.ServeMux.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
var AdminServerMux *PServeMux
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
AdminServerMux = &PServeMux{ServeMux: http.NewServeMux()}
|
AdminServerMux = server.NewRestMux("/admin")
|
||||||
// AdminServerMux.routes = make(map[string]map[string]http.HandlerFunc)
|
// AdminServerMux.routes = make(map[string]map[string]http.HandlerFunc)
|
||||||
for _, route := range AdminRoutes {
|
for _, route := range AdminRoutes {
|
||||||
AdminServerMux.Use(route)
|
AdminServerMux.HandleFunc(route.Method, route.Path, route.Handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -2,4 +2,8 @@ module git.pyer.club/kingecg/gohttpd
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require git.pyer.club/kingecg/gologger v1.0.0 // indirect
|
require (
|
||||||
|
git.pyer.club/kingecg/gologger v1.0.0 // indirect
|
||||||
|
github.com/samber/lo v1.39.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||||
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,6 @@
|
||||||
git.pyer.club/kingecg/gologger v1.0.0 h1:H3oFIJ1p7mlAgJgJ/wiM+hxn34x7IxY4YiafY8iMfAk=
|
git.pyer.club/kingecg/gologger v1.0.0 h1:H3oFIJ1p7mlAgJgJ/wiM+hxn34x7IxY4YiafY8iMfAk=
|
||||||
git.pyer.club/kingecg/gologger v1.0.0/go.mod h1:SNSl2jRHPzIpHSzdKOoVG798rtYMjPDPFyxUrEgivkY=
|
git.pyer.club/kingecg/gologger v1.0.0/go.mod h1:SNSl2jRHPzIpHSzdKOoVG798rtYMjPDPFyxUrEgivkY=
|
||||||
|
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||||
|
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||||
|
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
|
||||||
|
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||||
|
|
15
main.go
15
main.go
|
@ -2,8 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
admin "git.pyer.club/kingecg/gohttpd/admin"
|
admin "git.pyer.club/kingecg/gohttpd/admin"
|
||||||
|
"git.pyer.club/kingecg/gohttpd/server"
|
||||||
logger "git.pyer.club/kingecg/gologger"
|
logger "git.pyer.club/kingecg/gologger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,5 +42,14 @@ func main() {
|
||||||
})
|
})
|
||||||
defaultLogger := logger.GetLogger("default")
|
defaultLogger := logger.GetLogger("default")
|
||||||
defaultLogger.Info("Listening...")
|
defaultLogger.Info("Listening...")
|
||||||
http.ListenAndServe(":8080", admin.AdminServerMux)
|
serverMux := server.NewRestMux("/")
|
||||||
|
serverMux.HandleMux(admin.AdminServerMux)
|
||||||
|
go http.ListenAndServe(":8080", serverMux)
|
||||||
|
defaultLogger.Debug("Next")
|
||||||
|
var waiter = make(chan os.Signal, 1) // buffered channel
|
||||||
|
signal.Notify(waiter, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
|
||||||
|
// blocks here until there's a signal
|
||||||
|
<-waiter
|
||||||
|
defaultLogger.Info("Listened")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 可以嵌套的Rest http server mux
|
||||||
|
type RestMux struct {
|
||||||
|
Path string
|
||||||
|
imux *http.ServeMux
|
||||||
|
rmuxPaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, has := lo.Find[string](mux.rmuxPaths, func(s string) bool {
|
||||||
|
return strings.HasPrefix(r.URL.Path, s)
|
||||||
|
})
|
||||||
|
if has {
|
||||||
|
mux.imux.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.URL.Path = "/" + strings.ToLower(r.Method) + r.URL.Path
|
||||||
|
r.RequestURI = "/" + strings.ToLower(r.Method) + r.RequestURI
|
||||||
|
h, _ := mux.imux.Handler(r)
|
||||||
|
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
m := path
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
m = "/" + path
|
||||||
|
}
|
||||||
|
mux.imux.HandleFunc("/"+strings.ToLower(method)+m, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) Get(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("GET", path, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) Post(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("POST", path, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) Put(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("PUT", path, f)
|
||||||
|
}
|
||||||
|
func (mux *RestMux) Delete(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("DELETE", path, f)
|
||||||
|
}
|
||||||
|
func (mux *RestMux) Patch(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("PATCH", path, f)
|
||||||
|
}
|
||||||
|
func (mux *RestMux) Head(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("HEAD", path, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) Option(path string, f func(http.ResponseWriter, *http.Request)) {
|
||||||
|
mux.HandleFunc("OPTION", path, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mux *RestMux) HandleMux(nmux *RestMux) {
|
||||||
|
p := nmux.Path
|
||||||
|
if !strings.HasSuffix(p, "/") {
|
||||||
|
p = p + "/"
|
||||||
|
}
|
||||||
|
mux.imux.Handle(p, http.StripPrefix(nmux.Path, nmux))
|
||||||
|
mux.rmuxPaths = append(mux.rmuxPaths, nmux.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRestMux(path string) *RestMux {
|
||||||
|
return &RestMux{
|
||||||
|
Path: path,
|
||||||
|
imux: http.NewServeMux(),
|
||||||
|
rmuxPaths: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/go
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=go
|
||||||
|
|
||||||
|
### Go ###
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
### Go Patch ###
|
||||||
|
/vendor/
|
||||||
|
/Godeps/
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||||
|
|
||||||
|
cover.out
|
||||||
|
cover.html
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
.idea/
|
|
@ -0,0 +1,7 @@
|
||||||
|
language: go
|
||||||
|
before_install:
|
||||||
|
- go mod download
|
||||||
|
- make tools
|
||||||
|
go:
|
||||||
|
- "1.18"
|
||||||
|
script: make test
|
|
@ -0,0 +1,429 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
@samber: I sometimes forget to update this file. Ping me on [Twitter](https://twitter.com/samuelberthe) or open an issue in case of error. We need to keep a clear changelog for easier lib upgrade.
|
||||||
|
|
||||||
|
## 1.38.1 (2023-03-20)
|
||||||
|
|
||||||
|
Improvement:
|
||||||
|
- Async and AsyncX: now returns `<-chan T` instead of `chan T`
|
||||||
|
|
||||||
|
## 1.38.0 (2023-03-20)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.ValueOr
|
||||||
|
- lo.DebounceBy
|
||||||
|
- lo.EmptyableToPtr
|
||||||
|
|
||||||
|
Improvement:
|
||||||
|
- Substring: add support for non-english chars
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Async: Fix goroutine leak
|
||||||
|
|
||||||
|
## 1.37.0 (2022-12-15)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.PartialX
|
||||||
|
- lo.Transaction
|
||||||
|
|
||||||
|
Improvement:
|
||||||
|
- lo.Associate / lo.SliceToMap: faster memory allocation
|
||||||
|
|
||||||
|
Chore:
|
||||||
|
- Remove *_test.go files from releases, in order to cleanup dev dependencies
|
||||||
|
|
||||||
|
## 1.36.0 (2022-11-28)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.AttemptWhile
|
||||||
|
- lo.AttemptWhileWithDelay
|
||||||
|
|
||||||
|
## 1.35.0 (2022-11-15)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.RandomString
|
||||||
|
- lo.BufferWithTimeout (alias to lo.BatchWithTimeout)
|
||||||
|
- lo.Buffer (alias to lo.Batch)
|
||||||
|
|
||||||
|
Change:
|
||||||
|
- lo.Slice: avoid panic caused by out-of-bounds
|
||||||
|
|
||||||
|
Deprecation:
|
||||||
|
- lo.BatchWithTimeout
|
||||||
|
- lo.Batch
|
||||||
|
|
||||||
|
## 1.34.0 (2022-11-12)
|
||||||
|
|
||||||
|
Improving:
|
||||||
|
- lo.Union: faster and can receive more than 2 lists
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.FanIn (alias to lo.ChannelMerge)
|
||||||
|
- lo.FanOut
|
||||||
|
|
||||||
|
Deprecation:
|
||||||
|
- lo.ChannelMerge
|
||||||
|
|
||||||
|
## 1.33.0 (2022-10-14)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
- lo.ChannelMerge
|
||||||
|
|
||||||
|
Improving:
|
||||||
|
- helpers with callbacks/predicates/iteratee now have named arguments, for easier autocompletion
|
||||||
|
|
||||||
|
## 1.32.0 (2022-10-10)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ChannelToSlice
|
||||||
|
- lo.CountValues
|
||||||
|
- lo.CountValuesBy
|
||||||
|
- lo.MapEntries
|
||||||
|
- lo.Sum
|
||||||
|
- lo.Interleave
|
||||||
|
- TupleX.Unpack()
|
||||||
|
|
||||||
|
## 1.31.0 (2022-10-06)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.SliceToChannel
|
||||||
|
- lo.Generator
|
||||||
|
- lo.Batch
|
||||||
|
- lo.BatchWithTimeout
|
||||||
|
|
||||||
|
## 1.30.1 (2022-10-06)
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
|
||||||
|
- lo.Try1: remove generic type
|
||||||
|
- lo.Validate: format error properly
|
||||||
|
|
||||||
|
## 1.30.0 (2022-10-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.TernaryF
|
||||||
|
- lo.Validate
|
||||||
|
|
||||||
|
## 1.29.0 (2022-10-02)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ErrorAs
|
||||||
|
- lo.TryOr
|
||||||
|
- lo.TryOrX
|
||||||
|
|
||||||
|
## 1.28.0 (2022-09-05)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ChannelDispatcher with 6 dispatching strategies:
|
||||||
|
- lo.DispatchingStrategyRoundRobin
|
||||||
|
- lo.DispatchingStrategyRandom
|
||||||
|
- lo.DispatchingStrategyWeightedRandom
|
||||||
|
- lo.DispatchingStrategyFirst
|
||||||
|
- lo.DispatchingStrategyLeast
|
||||||
|
- lo.DispatchingStrategyMost
|
||||||
|
|
||||||
|
## 1.27.1 (2022-08-15)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
|
||||||
|
- Removed comparable constraint for lo.FindKeyBy
|
||||||
|
|
||||||
|
## 1.27.0 (2022-07-29)
|
||||||
|
|
||||||
|
Breaking:
|
||||||
|
|
||||||
|
- Change of MapToSlice prototype: `MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(V, K) R) []R` -> `MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(K, V) R) []R`
|
||||||
|
|
||||||
|
Added:
|
||||||
|
|
||||||
|
- lo.ChunkString
|
||||||
|
- lo.SliceToMap (alias to lo.Associate)
|
||||||
|
|
||||||
|
## 1.26.0 (2022-07-24)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Associate
|
||||||
|
- lo.ReduceRight
|
||||||
|
- lo.FromPtrOr
|
||||||
|
- lo.MapToSlice
|
||||||
|
- lo.IsSorted
|
||||||
|
- lo.IsSortedByKey
|
||||||
|
|
||||||
|
## 1.25.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.FindUniques
|
||||||
|
- lo.FindUniquesBy
|
||||||
|
- lo.FindDuplicates
|
||||||
|
- lo.FindDuplicatesBy
|
||||||
|
- lo.IsNotEmpty
|
||||||
|
|
||||||
|
## 1.24.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Without
|
||||||
|
- lo.WithoutEmpty
|
||||||
|
|
||||||
|
## 1.23.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.FindKey
|
||||||
|
- lo.FindKeyBy
|
||||||
|
|
||||||
|
## 1.22.0 (2022-07-04)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Slice
|
||||||
|
- lo.FromPtr
|
||||||
|
- lo.IsEmpty
|
||||||
|
- lo.Compact
|
||||||
|
- lo.ToPairs: alias to lo.Entries
|
||||||
|
- lo.FromPairs: alias to lo.FromEntries
|
||||||
|
- lo.Partial
|
||||||
|
|
||||||
|
Change:
|
||||||
|
|
||||||
|
- lo.Must + lo.MustX: add context to panic message
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
|
||||||
|
- lo.Nth: out of bound exception (#137)
|
||||||
|
|
||||||
|
## 1.21.0 (2022-05-10)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.ToAnySlice
|
||||||
|
- lo.FromAnySlice
|
||||||
|
|
||||||
|
## 1.20.0 (2022-05-02)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Synchronize
|
||||||
|
- lo.SumBy
|
||||||
|
|
||||||
|
Change:
|
||||||
|
- Removed generic type definition for lo.Try0: `lo.Try0[T]()` -> `lo.Try0()`
|
||||||
|
|
||||||
|
## 1.19.0 (2022-04-30)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.RepeatBy
|
||||||
|
- lo.Subset
|
||||||
|
- lo.Replace
|
||||||
|
- lo.ReplaceAll
|
||||||
|
- lo.Substring
|
||||||
|
- lo.RuneLength
|
||||||
|
|
||||||
|
## 1.18.0 (2022-04-28)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.SomeBy
|
||||||
|
- lo.EveryBy
|
||||||
|
- lo.None
|
||||||
|
- lo.NoneBy
|
||||||
|
|
||||||
|
## 1.17.0 (2022-04-27)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Unpack2 -> lo.Unpack3
|
||||||
|
- lo.Async0 -> lo.Async6
|
||||||
|
|
||||||
|
## 1.16.0 (2022-04-26)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.AttemptWithDelay
|
||||||
|
|
||||||
|
## 1.15.0 (2022-04-22)
|
||||||
|
|
||||||
|
Improvement:
|
||||||
|
|
||||||
|
- lo.Must: error or boolean value
|
||||||
|
|
||||||
|
## 1.14.0 (2022-04-21)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- lo.Coalesce
|
||||||
|
|
||||||
|
## 1.13.0 (2022-04-14)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- PickBy
|
||||||
|
- PickByKeys
|
||||||
|
- PickByValues
|
||||||
|
- OmitBy
|
||||||
|
- OmitByKeys
|
||||||
|
- OmitByValues
|
||||||
|
- Clamp
|
||||||
|
- MapKeys
|
||||||
|
- Invert
|
||||||
|
- IfF + ElseIfF + ElseF
|
||||||
|
- T0() + T1() + T2() + T3() + ...
|
||||||
|
|
||||||
|
## 1.12.0 (2022-04-12)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- Must
|
||||||
|
- Must{0-6}
|
||||||
|
- FindOrElse
|
||||||
|
- Async
|
||||||
|
- MinBy
|
||||||
|
- MaxBy
|
||||||
|
- Count
|
||||||
|
- CountBy
|
||||||
|
- FindIndexOf
|
||||||
|
- FindLastIndexOf
|
||||||
|
- FilterMap
|
||||||
|
|
||||||
|
## 1.11.0 (2022-03-11)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- Try
|
||||||
|
- Try{0-6}
|
||||||
|
- TryWitchValue
|
||||||
|
- TryCatch
|
||||||
|
- TryCatchWitchValue
|
||||||
|
- Debounce
|
||||||
|
- Reject
|
||||||
|
|
||||||
|
## 1.10.0 (2022-03-11)
|
||||||
|
|
||||||
|
Adding:
|
||||||
|
|
||||||
|
- Range
|
||||||
|
- RangeFrom
|
||||||
|
- RangeWithSteps
|
||||||
|
|
||||||
|
## 1.9.0 (2022-03-10)
|
||||||
|
|
||||||
|
Added
|
||||||
|
|
||||||
|
- Drop
|
||||||
|
- DropRight
|
||||||
|
- DropWhile
|
||||||
|
- DropRightWhile
|
||||||
|
|
||||||
|
## 1.8.0 (2022-03-10)
|
||||||
|
|
||||||
|
Adding Union.
|
||||||
|
|
||||||
|
## 1.7.0 (2022-03-09)
|
||||||
|
|
||||||
|
Adding ContainBy
|
||||||
|
|
||||||
|
Adding MapValues
|
||||||
|
|
||||||
|
Adding FlatMap
|
||||||
|
|
||||||
|
## 1.6.0 (2022-03-07)
|
||||||
|
|
||||||
|
Fixed PartitionBy.
|
||||||
|
|
||||||
|
Adding Sample
|
||||||
|
|
||||||
|
Adding Samples
|
||||||
|
|
||||||
|
## 1.5.0 (2022-03-07)
|
||||||
|
|
||||||
|
Adding Times
|
||||||
|
|
||||||
|
Adding Attempt
|
||||||
|
|
||||||
|
Adding Repeat
|
||||||
|
|
||||||
|
## 1.4.0 (2022-03-07)
|
||||||
|
|
||||||
|
- adding tuple types (2->9)
|
||||||
|
- adding Zip + Unzip
|
||||||
|
- adding lo.PartitionBy + lop.PartitionBy
|
||||||
|
- adding lop.GroupBy
|
||||||
|
- fixing Nth
|
||||||
|
|
||||||
|
## 1.3.0 (2022-03-03)
|
||||||
|
|
||||||
|
Last and Nth return errors
|
||||||
|
|
||||||
|
## 1.2.0 (2022-03-03)
|
||||||
|
|
||||||
|
Adding `lop.Map` and `lop.ForEach`.
|
||||||
|
|
||||||
|
## 1.1.0 (2022-03-03)
|
||||||
|
|
||||||
|
Adding `i int` param to `lo.Map()`, `lo.Filter()`, `lo.ForEach()` and `lo.Reduce()` predicates.
|
||||||
|
|
||||||
|
## 1.0.0 (2022-03-02)
|
||||||
|
|
||||||
|
*Initial release*
|
||||||
|
|
||||||
|
Supported helpers for slices:
|
||||||
|
|
||||||
|
- Filter
|
||||||
|
- Map
|
||||||
|
- Reduce
|
||||||
|
- ForEach
|
||||||
|
- Uniq
|
||||||
|
- UniqBy
|
||||||
|
- GroupBy
|
||||||
|
- Chunk
|
||||||
|
- Flatten
|
||||||
|
- Shuffle
|
||||||
|
- Reverse
|
||||||
|
- Fill
|
||||||
|
- ToMap
|
||||||
|
|
||||||
|
Supported helpers for maps:
|
||||||
|
|
||||||
|
- Keys
|
||||||
|
- Values
|
||||||
|
- Entries
|
||||||
|
- FromEntries
|
||||||
|
- Assign (maps merge)
|
||||||
|
|
||||||
|
Supported intersection helpers:
|
||||||
|
|
||||||
|
- Contains
|
||||||
|
- Every
|
||||||
|
- Some
|
||||||
|
- Intersect
|
||||||
|
- Difference
|
||||||
|
|
||||||
|
Supported search helpers:
|
||||||
|
|
||||||
|
- IndexOf
|
||||||
|
- LastIndexOf
|
||||||
|
- Find
|
||||||
|
- Min
|
||||||
|
- Max
|
||||||
|
- Last
|
||||||
|
- Nth
|
||||||
|
|
||||||
|
Other functional programming helpers:
|
||||||
|
|
||||||
|
- Ternary (1 line if/else statement)
|
||||||
|
- If / ElseIf / Else
|
||||||
|
- Switch / Case / Default
|
||||||
|
- ToPtr
|
||||||
|
- ToSlicePtr
|
||||||
|
|
||||||
|
Constraints:
|
||||||
|
|
||||||
|
- Clonable
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
FROM golang:1.18
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/samber/lo
|
||||||
|
|
||||||
|
COPY Makefile go.* ./
|
||||||
|
|
||||||
|
RUN make tools
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Samuel Berthe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
BIN=go
|
||||||
|
|
||||||
|
build:
|
||||||
|
${BIN} build -v ./...
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -race -v ./...
|
||||||
|
watch-test:
|
||||||
|
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
||||||
|
|
||||||
|
bench:
|
||||||
|
go test -benchmem -count 3 -bench ./...
|
||||||
|
watch-bench:
|
||||||
|
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
${BIN} test -v -coverprofile=cover.out -covermode=atomic .
|
||||||
|
${BIN} tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
|
# tools
|
||||||
|
tools:
|
||||||
|
${BIN} install github.com/cespare/reflex@latest
|
||||||
|
${BIN} install github.com/rakyll/gotest@latest
|
||||||
|
${BIN} install github.com/psampaz/go-mod-outdated@latest
|
||||||
|
${BIN} install github.com/jondot/goweight@latest
|
||||||
|
${BIN} install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||||
|
${BIN} get -t -u golang.org/x/tools/cmd/cover
|
||||||
|
${BIN} install github.com/sonatype-nexus-community/nancy@latest
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
lint:
|
||||||
|
golangci-lint run --timeout 60s --max-same-issues 50 ./...
|
||||||
|
lint-fix:
|
||||||
|
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
||||||
|
|
||||||
|
audit: tools
|
||||||
|
${BIN} list -json -m all | nancy sleuth
|
||||||
|
|
||||||
|
outdated: tools
|
||||||
|
${BIN} list -u -m -json all | go-mod-outdated -update -direct
|
||||||
|
|
||||||
|
weight: tools
|
||||||
|
goweight
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,309 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
children := createChannels[T](count, channelBufferCap)
|
||||||
|
|
||||||
|
roChildren := channelsToReadOnly(children)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// propagate channel closing to children
|
||||||
|
defer closeChannels(children)
|
||||||
|
|
||||||
|
var i uint64 = 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg, ok := <-stream
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destination := strategy(msg, i, roChildren) % count
|
||||||
|
children[destination] <- msg
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return roChildren
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChannels[T any](count int, channelBufferCap int) []chan T {
|
||||||
|
children := make([]chan T, 0, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
children = append(children, make(chan T, channelBufferCap))
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
func channelsToReadOnly[T any](children []chan T) []<-chan T {
|
||||||
|
roChildren := make([]<-chan T, 0, len(children))
|
||||||
|
|
||||||
|
for i := range children {
|
||||||
|
roChildren = append(roChildren, children[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return roChildren
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeChannels[T any](children []chan T) {
|
||||||
|
for i := 0; i < len(children); i++ {
|
||||||
|
close(children[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func channelIsNotFull[T any](ch <-chan T) bool {
|
||||||
|
return cap(ch) == 0 || len(ch) < cap(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner.
|
||||||
|
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||||
|
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := int(index % uint64(len(channels)))
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyRandom distributes messages in a random manner.
|
||||||
|
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||||
|
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := rand.Intn(len(channels))
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyWeightedRandom distributes messages in a weighted manner.
|
||||||
|
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||||
|
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
|
||||||
|
seq := []int{}
|
||||||
|
|
||||||
|
for i := 0; i < len(weights); i++ {
|
||||||
|
for j := 0; j < weights[i]; j++ {
|
||||||
|
seq = append(seq, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
i := seq[rand.Intn(len(seq))]
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int {
|
||||||
|
for {
|
||||||
|
for i := range channels {
|
||||||
|
if channelIsNotFull(channels[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyLeast distributes messages in the emptiest channel.
|
||||||
|
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])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchingStrategyMost distributes messages in the fullest channel.
|
||||||
|
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||||
|
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])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceToChannel returns a read-only channels of collection elements.
|
||||||
|
func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
|
||||||
|
ch := make(chan T, bufferSize)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _, item := range collection {
|
||||||
|
ch <- item
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelToSlice returns a slice built from channels items. Blocks until channel closes.
|
||||||
|
func ChannelToSlice[T any](ch <-chan T) []T {
|
||||||
|
collection := []T{}
|
||||||
|
|
||||||
|
for item := range ch {
|
||||||
|
collection = append(collection, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator implements the generator design pattern.
|
||||||
|
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
|
||||||
|
ch := make(chan T, bufferSize)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// WARNING: infinite loop
|
||||||
|
generator(func(t T) {
|
||||||
|
ch <- t
|
||||||
|
})
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for ; index < size; index++ {
|
||||||
|
item, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return buffer, index, time.Since(now), false
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = append(buffer, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch creates a slice of n elements from a channel. Returns the slice and the slice length.
|
||||||
|
//
|
||||||
|
// Deprecated: Use [Buffer] instead.
|
||||||
|
func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||||
|
return Buffer(ch, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
|
||||||
|
// @TODO: we should probably provide an helper that reuse the same buffer.
|
||||||
|
func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||||
|
expire := time.NewTimer(timeout)
|
||||||
|
defer expire.Stop()
|
||||||
|
|
||||||
|
buffer := make([]T, 0, size)
|
||||||
|
index := 0
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for ; index < size; index++ {
|
||||||
|
select {
|
||||||
|
case item, ok := <-ch:
|
||||||
|
if !ok {
|
||||||
|
return buffer, index, time.Since(now), false
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = append(buffer, item)
|
||||||
|
|
||||||
|
case <-expire.C:
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, index, time.Since(now), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
|
||||||
|
//
|
||||||
|
// Deprecated: Use [BufferWithTimeout] instead.
|
||||||
|
func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||||
|
return BufferWithTimeout(ch, size, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||||
|
out := make(chan T, channelBufferCap)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Start an output goroutine for each input channel in upstreams.
|
||||||
|
wg.Add(len(upstreams))
|
||||||
|
for _, c := range upstreams {
|
||||||
|
go func(c <-chan T) {
|
||||||
|
for n := range c {
|
||||||
|
out <- n
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a goroutine to close out once all the output goroutines are done.
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(out)
|
||||||
|
}()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Deprecated: Use [FanIn] instead.
|
||||||
|
func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||||
|
return FanIn(channelBufferCap, upstreams...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T {
|
||||||
|
downstreams := createChannels[T](count, channelsBufferCap)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range upstream {
|
||||||
|
for i := range downstreams {
|
||||||
|
downstreams[i] <- msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close out once all the output goroutines are done.
|
||||||
|
for i := range downstreams {
|
||||||
|
close(downstreams[i])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return channelsToReadOnly(downstreams)
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type synchronize struct {
|
||||||
|
locker sync.Locker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *synchronize) Do(cb func()) {
|
||||||
|
s.locker.Lock()
|
||||||
|
Try0(cb)
|
||||||
|
s.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize wraps the underlying callback in a mutex. It receives an optional mutex.
|
||||||
|
func Synchronize(opt ...sync.Locker) *synchronize {
|
||||||
|
if len(opt) > 1 {
|
||||||
|
panic("unexpected arguments")
|
||||||
|
} else if len(opt) == 0 {
|
||||||
|
opt = append(opt, &sync.Mutex{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &synchronize{
|
||||||
|
locker: opt[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async executes a function in a goroutine and returns the result in a channel.
|
||||||
|
func Async[A any](f func() A) <-chan A {
|
||||||
|
ch := make(chan A, 1)
|
||||||
|
go func() {
|
||||||
|
ch <- f()
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async0 executes a function in a goroutine and returns a channel set once the function finishes.
|
||||||
|
func Async0(f func()) <-chan struct{} {
|
||||||
|
ch := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
f()
|
||||||
|
ch <- struct{}{}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async1 is an alias to Async.
|
||||||
|
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.
|
||||||
|
func Async2[A any, B any](f func() (A, B)) <-chan Tuple2[A, B] {
|
||||||
|
ch := make(chan Tuple2[A, B], 1)
|
||||||
|
go func() {
|
||||||
|
ch <- T2(f())
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
|
||||||
|
func Async3[A any, B any, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
|
||||||
|
ch := make(chan Tuple3[A, B, C], 1)
|
||||||
|
go func() {
|
||||||
|
ch <- T3(f())
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
|
||||||
|
func Async4[A any, B any, C any, 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() {
|
||||||
|
ch <- T4(f())
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
|
||||||
|
func Async5[A any, B any, C any, D any, 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() {
|
||||||
|
ch <- T5(f())
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
|
||||||
|
func Async6[A any, B any, C any, D any, E any, 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() {
|
||||||
|
ch <- T6(f())
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Ternary is a 1 line if/else statement.
|
||||||
|
// Play: https://go.dev/play/p/t-D7WBL44h2
|
||||||
|
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
||||||
|
if condition {
|
||||||
|
return ifOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
return elseOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
// TernaryF is a 1 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 {
|
||||||
|
if condition {
|
||||||
|
return ifFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
return elseFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ifElse[T any] struct {
|
||||||
|
result T
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// If.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func If[T any](condition bool, result T) *ifElse[T] {
|
||||||
|
if condition {
|
||||||
|
return &ifElse[T]{result, true}
|
||||||
|
}
|
||||||
|
|
||||||
|
var t T
|
||||||
|
return &ifElse[T]{t, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func IfF[T any](condition bool, resultF func() T) *ifElse[T] {
|
||||||
|
if condition {
|
||||||
|
return &ifElse[T]{resultF(), true}
|
||||||
|
}
|
||||||
|
|
||||||
|
var t T
|
||||||
|
return &ifElse[T]{t, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElseIf.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
|
||||||
|
if !i.done && condition {
|
||||||
|
i.result = result
|
||||||
|
i.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElseIfF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func (i *ifElse[T]) ElseIfF(condition bool, resultF func() T) *ifElse[T] {
|
||||||
|
if !i.done && condition {
|
||||||
|
i.result = resultF()
|
||||||
|
i.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func (i *ifElse[T]) Else(result T) T {
|
||||||
|
if i.done {
|
||||||
|
return i.result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElseF.
|
||||||
|
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||||
|
func (i *ifElse[T]) ElseF(resultF func() T) T {
|
||||||
|
if i.done {
|
||||||
|
return i.result
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultF()
|
||||||
|
}
|
||||||
|
|
||||||
|
type switchCase[T comparable, R any] struct {
|
||||||
|
predicate T
|
||||||
|
result R
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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] {
|
||||||
|
var result R
|
||||||
|
|
||||||
|
return &switchCase[T, R]{
|
||||||
|
predicate,
|
||||||
|
result,
|
||||||
|
false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
|
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
|
||||||
|
if !s.done && s.predicate == val {
|
||||||
|
s.result = result
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaseF.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
|
func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
|
||||||
|
if !s.done && s.predicate == val {
|
||||||
|
s.result = cb()
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
|
func (s *switchCase[T, R]) Default(result R) R {
|
||||||
|
if !s.done {
|
||||||
|
s.result = result
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.result
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultF.
|
||||||
|
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||||
|
func (s *switchCase[T, R]) DefaultF(cb func() R) R {
|
||||||
|
if !s.done {
|
||||||
|
s.result = cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.result
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Clonable defines a constraint of types having Clone() T method.
|
||||||
|
type Clonable[T any] interface {
|
||||||
|
Clone() T
|
||||||
|
}
|
|
@ -0,0 +1,354 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate is a helper that creates an error when a condition is not met.
|
||||||
|
// Play: https://go.dev/play/p/vPyh51XpCBt
|
||||||
|
func Validate(ok bool, format string, args ...any) error {
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||||
|
if len(msgAndArgs) == 1 {
|
||||||
|
if msgAsStr, ok := msgAndArgs[0].(string); ok {
|
||||||
|
return msgAsStr
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%+v", msgAndArgs[0])
|
||||||
|
}
|
||||||
|
if len(msgAndArgs) > 1 {
|
||||||
|
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// must panics if err is error or false.
|
||||||
|
func must(err any, messageArgs ...interface{}) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := err.(type) {
|
||||||
|
case bool:
|
||||||
|
if !e {
|
||||||
|
message := messageFromMsgAndArgs(messageArgs...)
|
||||||
|
if message == "" {
|
||||||
|
message = "not ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
case error:
|
||||||
|
message := messageFromMsgAndArgs(messageArgs...)
|
||||||
|
if message != "" {
|
||||||
|
panic(message + ": " + e.Error())
|
||||||
|
} else {
|
||||||
|
panic(e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func Must[T any](val T, err any, messageArgs ...interface{}) T {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must0 has the same behavior as Must, but callback returns no variable.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must0(err any, messageArgs ...interface{}) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must1 is an alias to Must
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must1[T any](val T, err any, messageArgs ...interface{}) T {
|
||||||
|
return Must(val, err, messageArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must2 has the same behavior as Must, but callback returns 2 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must2[T1 any, T2 any](val1 T1, val2 T2, err any, messageArgs ...interface{}) (T1, T2) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must3 has the same behavior as Must, but callback returns 3 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must3[T1 any, T2 any, T3 any](val1 T1, val2 T2, val3 T3, err any, messageArgs ...interface{}) (T1, T2, T3) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must4 has the same behavior as Must, but callback returns 4 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must4[T1 any, T2 any, T3 any, T4 any](val1 T1, val2 T2, val3 T3, val4 T4, err any, messageArgs ...interface{}) (T1, T2, T3, T4) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3, val4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must5 has the same behavior as Must, but callback returns 5 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must5[T1 any, T2 any, T3 any, T4 any, T5 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, err any, messageArgs ...interface{}) (T1, T2, T3, T4, T5) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3, val4, val5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must6 has the same behavior as Must, but callback returns 6 variables.
|
||||||
|
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||||
|
func Must6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, val6 T6, err any, messageArgs ...interface{}) (T1, T2, T3, T4, T5, T6) {
|
||||||
|
must(err, messageArgs...)
|
||||||
|
return val1, val2, val3, val4, val5, val6
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try calls the function and return false in case of error.
|
||||||
|
func Try(callback func() error) (ok bool) {
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := callback()
|
||||||
|
if err != nil {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try0 has the same behavior as Try, but callback returns no variable.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try0(callback func()) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
callback()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try1 is an alias to Try.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try1(callback func() error) bool {
|
||||||
|
return Try(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try2 has the same behavior as Try, but callback returns 2 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try2[T any](callback func() (T, error)) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
_, err := callback()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try3 has the same behavior as Try, but callback returns 3 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try3[T, R any](callback func() (T, R, error)) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
_, _, err := callback()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try4 has the same behavior as Try, but callback returns 4 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try4[T, R, S any](callback func() (T, R, S, error)) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
_, _, _, err := callback()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try5 has the same behavior as Try, but callback returns 5 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try5[T, R, S, Q any](callback func() (T, R, S, Q, error)) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
_, _, _, _, err := callback()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try6 has the same behavior as Try, but callback returns 6 variables.
|
||||||
|
// Play: https://go.dev/play/p/mTyyWUvn9u4
|
||||||
|
func Try6[T, R, S, Q, U any](callback func() (T, R, S, Q, U, error)) bool {
|
||||||
|
return Try(func() error {
|
||||||
|
_, _, _, _, _, err := callback()
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr[A any](callback func() (A, error), fallbackA A) (A, bool) {
|
||||||
|
return TryOr1(callback, fallbackA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr1 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr1[A any](callback func() (A, error), fallbackA A) (A, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr2 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr2[A any, B any](callback func() (A, B, error), fallbackA A, fallbackB B) (A, B, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr3 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr3[A any, B any, C any](callback func() (A, B, C, error), fallbackA A, fallbackB B, fallbackC C) (A, B, C, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr4 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr4[A any, B any, C any, D any](callback func() (A, B, C, D, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D) (A, B, C, D, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr5 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr5[A any, B any, C any, D any, E any](callback func() (A, B, C, D, E, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E) (A, B, C, D, E, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, e, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
fallbackE = e
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryOr6 has the same behavior as Must, but returns a default value in case of error.
|
||||||
|
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
|
||||||
|
func TryOr6[A any, B any, C any, D any, E any, F any](callback func() (A, B, C, D, E, F, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E, fallbackF F) (A, B, C, D, E, F, bool) {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
Try0(func() {
|
||||||
|
a, b, c, d, e, f, err := callback()
|
||||||
|
if err == nil {
|
||||||
|
fallbackA = a
|
||||||
|
fallbackB = b
|
||||||
|
fallbackC = c
|
||||||
|
fallbackD = d
|
||||||
|
fallbackE = e
|
||||||
|
fallbackF = f
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, fallbackF, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryWithErrorValue has the same behavior as Try, but also returns value passed to panic.
|
||||||
|
// Play: https://go.dev/play/p/Kc7afQIT2Fs
|
||||||
|
func TryWithErrorValue(callback func() error) (errorValue any, ok bool) {
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ok = false
|
||||||
|
errorValue = r
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := callback()
|
||||||
|
if err != nil {
|
||||||
|
ok = false
|
||||||
|
errorValue = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryCatch has the same behavior as Try, but calls the catch function in case of error.
|
||||||
|
// Play: https://go.dev/play/p/PnOON-EqBiU
|
||||||
|
func TryCatch(callback func() error, catch func()) {
|
||||||
|
if !Try(callback) {
|
||||||
|
catch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryCatchWithErrorValue has the same behavior as TryWithErrorValue, but calls the catch function in case of error.
|
||||||
|
// Play: https://go.dev/play/p/8Pc9gwX_GZO
|
||||||
|
func TryCatchWithErrorValue(callback func() error, catch func(any)) {
|
||||||
|
if err, ok := TryWithErrorValue(callback); !ok {
|
||||||
|
catch(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorsAs is a shortcut for errors.As(err, &&T).
|
||||||
|
// Play: https://go.dev/play/p/8wk5rH8UfrE
|
||||||
|
func ErrorsAs[T error](err error) (T, bool) {
|
||||||
|
var t T
|
||||||
|
ok := errors.As(err, &t)
|
||||||
|
return t, ok
|
||||||
|
}
|
|
@ -0,0 +1,372 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// import "golang.org/x/exp/constraints"
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func IndexOf[T comparable](collection []T, element T) int {
|
||||||
|
for i, item := range collection {
|
||||||
|
if item == element {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func LastIndexOf[T comparable](collection []T, element T) int {
|
||||||
|
length := len(collection)
|
||||||
|
|
||||||
|
for i := length - 1; i >= 0; i-- {
|
||||||
|
if collection[i] == element {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find search an element in a slice based on a predicate. It returns element and true if element was found.
|
||||||
|
func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
|
||||||
|
for _, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
return item, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result T
|
||||||
|
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.
|
||||||
|
func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
|
||||||
|
for i, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
return item, i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result T
|
||||||
|
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.
|
||||||
|
func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
|
||||||
|
length := len(collection)
|
||||||
|
|
||||||
|
for i := length - 1; i >= 0; i-- {
|
||||||
|
if predicate(collection[i]) {
|
||||||
|
return collection[i], i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result T
|
||||||
|
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.
|
||||||
|
func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) T {
|
||||||
|
for _, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, v := range object {
|
||||||
|
if v == value {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty[K](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindKeyBy returns the key of the first element predicate returns truthy for.
|
||||||
|
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool) {
|
||||||
|
for k, v := range object {
|
||||||
|
if predicate(k, v) {
|
||||||
|
return k, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty[K](), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 collection.
|
||||||
|
func FindUniques[T comparable](collection []T) []T {
|
||||||
|
isDupl := make(map[T]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
duplicated, ok := isDupl[item]
|
||||||
|
if !ok {
|
||||||
|
isDupl[item] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[item] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if duplicated := isDupl[item]; !duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
func FindUniquesBy[T any, U comparable](collection []T, iteratee func(item T) U) []T {
|
||||||
|
isDupl := make(map[U]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
duplicated, ok := isDupl[key]
|
||||||
|
if !ok {
|
||||||
|
isDupl[key] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
if duplicated := isDupl[key]; !duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 collection.
|
||||||
|
func FindDuplicates[T comparable](collection []T) []T {
|
||||||
|
isDupl := make(map[T]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
duplicated, ok := isDupl[item]
|
||||||
|
if !ok {
|
||||||
|
isDupl[item] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[item] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if duplicated := isDupl[item]; duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
isDupl[item] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
func FindDuplicatesBy[T any, U comparable](collection []T, iteratee func(item T) U) []T {
|
||||||
|
isDupl := make(map[U]bool, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
duplicated, ok := isDupl[key]
|
||||||
|
if !ok {
|
||||||
|
isDupl[key] = false
|
||||||
|
} else if !duplicated {
|
||||||
|
isDupl[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-len(isDupl))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
if duplicated := isDupl[key]; duplicated {
|
||||||
|
result = append(result, item)
|
||||||
|
isDupl[key] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min search the minimum value of a collection.
|
||||||
|
// Returns zero value when collection is empty.
|
||||||
|
func Min[T constraints.Ordered](collection []T) T {
|
||||||
|
var min T
|
||||||
|
|
||||||
|
if len(collection) == 0 {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
min = collection[0]
|
||||||
|
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
item := collection[i]
|
||||||
|
|
||||||
|
if item < min {
|
||||||
|
min = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 collection is empty.
|
||||||
|
func MinBy[T any](collection []T, comparison func(a T, b T) bool) T {
|
||||||
|
var min T
|
||||||
|
|
||||||
|
if len(collection) == 0 {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
min = collection[0]
|
||||||
|
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
item := collection[i]
|
||||||
|
|
||||||
|
if comparison(item, min) {
|
||||||
|
min = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max searches the maximum value of a collection.
|
||||||
|
// Returns zero value when collection is empty.
|
||||||
|
func Max[T constraints.Ordered](collection []T) T {
|
||||||
|
var max T
|
||||||
|
|
||||||
|
if len(collection) == 0 {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
max = collection[0]
|
||||||
|
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
item := collection[i]
|
||||||
|
|
||||||
|
if item > max {
|
||||||
|
max = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 collection is empty.
|
||||||
|
func MaxBy[T any](collection []T, comparison func(a T, b T) bool) T {
|
||||||
|
var max T
|
||||||
|
|
||||||
|
if len(collection) == 0 {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
max = collection[0]
|
||||||
|
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
item := collection[i]
|
||||||
|
|
||||||
|
if comparison(item, max) {
|
||||||
|
max = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last returns the last element of a collection or error if empty.
|
||||||
|
func Last[T any](collection []T) (T, error) {
|
||||||
|
length := len(collection)
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
var t T
|
||||||
|
return t, fmt.Errorf("last: cannot extract the last element of an empty slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection[length-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
|
||||||
|
n := int(nth)
|
||||||
|
l := len(collection)
|
||||||
|
if n >= l || -n > l {
|
||||||
|
var t T
|
||||||
|
return t, fmt.Errorf("nth: %d out of slice bounds", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n >= 0 {
|
||||||
|
return collection[n], nil
|
||||||
|
}
|
||||||
|
return collection[l+n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample returns a random item from collection.
|
||||||
|
func Sample[T any](collection []T) T {
|
||||||
|
size := len(collection)
|
||||||
|
if size == 0 {
|
||||||
|
return Empty[T]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection[rand.Intn(size)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples returns N random unique items from collection.
|
||||||
|
func Samples[T any](collection []T, count int) []T {
|
||||||
|
size := len(collection)
|
||||||
|
|
||||||
|
copy := append([]T{}, collection...)
|
||||||
|
|
||||||
|
results := []T{}
|
||||||
|
|
||||||
|
for i := 0; i < size && i < count; i++ {
|
||||||
|
copyLength := size - i
|
||||||
|
|
||||||
|
index := rand.Intn(size - i)
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Partial returns new function that, when called, has its first argument set to the provided value.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial1 returns new function that, when called, has its first argument set to the provided value.
|
||||||
|
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.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial3 returns new function that, when called, has its first argument set to the provided value.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial4 returns new function that, when called, has its first argument set to the provided value.
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Contains returns true if an element is present in a collection.
|
||||||
|
func Contains[T comparable](collection []T, element T) bool {
|
||||||
|
for _, item := range collection {
|
||||||
|
if item == element {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsBy returns true if predicate function return true.
|
||||||
|
func ContainsBy[T any](collection []T, predicate func(item T) bool) bool {
|
||||||
|
for _, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
for _, elem := range subset {
|
||||||
|
if !Contains(collection, elem) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// EveryBy returns true if the predicate returns true for all of the elements in the collection or if the collection is empty.
|
||||||
|
func EveryBy[T any](collection []T, predicate func(item T) bool) bool {
|
||||||
|
for _, v := range collection {
|
||||||
|
if !predicate(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some returns true if at least 1 element of a subset is contained into a collection.
|
||||||
|
// If the subset is empty Some returns false.
|
||||||
|
func Some[T comparable](collection []T, subset []T) bool {
|
||||||
|
for _, elem := range subset {
|
||||||
|
if Contains(collection, elem) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
|
||||||
|
// If the collection is empty SomeBy returns false.
|
||||||
|
func SomeBy[T any](collection []T, predicate func(item T) bool) bool {
|
||||||
|
for _, v := range collection {
|
||||||
|
if predicate(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
for _, elem := range subset {
|
||||||
|
if Contains(collection, elem) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
|
||||||
|
func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
|
||||||
|
for _, v := range collection {
|
||||||
|
if predicate(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersect returns the intersection between two collections.
|
||||||
|
func Intersect[T comparable](list1 []T, list2 []T) []T {
|
||||||
|
result := []T{}
|
||||||
|
seen := map[T]struct{}{}
|
||||||
|
|
||||||
|
for _, elem := range list1 {
|
||||||
|
seen[elem] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elem := range list2 {
|
||||||
|
if _, ok := seen[elem]; ok {
|
||||||
|
result = append(result, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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](list1 []T, list2 []T) ([]T, []T) {
|
||||||
|
left := []T{}
|
||||||
|
right := []T{}
|
||||||
|
|
||||||
|
seenLeft := map[T]struct{}{}
|
||||||
|
seenRight := map[T]struct{}{}
|
||||||
|
|
||||||
|
for _, elem := range list1 {
|
||||||
|
seenLeft[elem] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elem := range list2 {
|
||||||
|
seenRight[elem] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elem := range list1 {
|
||||||
|
if _, ok := seenRight[elem]; !ok {
|
||||||
|
left = append(left, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elem := range list2 {
|
||||||
|
if _, ok := seenLeft[elem]; !ok {
|
||||||
|
right = append(right, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return left, right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union returns all distinct elements from given collections.
|
||||||
|
// result returns will not change the order of elements relatively.
|
||||||
|
func Union[T comparable](lists ...[]T) []T {
|
||||||
|
result := []T{}
|
||||||
|
seen := map[T]struct{}{}
|
||||||
|
|
||||||
|
for _, list := range lists {
|
||||||
|
for _, e := range list {
|
||||||
|
if _, ok := seen[e]; !ok {
|
||||||
|
seen[e] = struct{}{}
|
||||||
|
result = append(result, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without returns slice excluding all given values.
|
||||||
|
func Without[T comparable](collection []T, exclude ...T) []T {
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
for _, e := range collection {
|
||||||
|
if !Contains(exclude, e) {
|
||||||
|
result = append(result, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutEmpty returns slice excluding empty values.
|
||||||
|
func WithoutEmpty[T comparable](collection []T) []T {
|
||||||
|
var empty T
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
for _, e := range collection {
|
||||||
|
if e != empty {
|
||||||
|
result = append(result, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,224 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Keys creates an array of the map keys.
|
||||||
|
// Play: https://go.dev/play/p/Uu11fHASqrU
|
||||||
|
func Keys[K comparable, V any](in map[K]V) []K {
|
||||||
|
result := make([]K, 0, len(in))
|
||||||
|
|
||||||
|
for k := range in {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values creates an array of the map values.
|
||||||
|
// Play: https://go.dev/play/p/nnRTQkzQfF6
|
||||||
|
func Values[K comparable, V any](in map[K]V) []V {
|
||||||
|
result := make([]V, 0, len(in))
|
||||||
|
|
||||||
|
for _, v := range in {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueOr returns the value of the given key or the fallback value if the key is not present.
|
||||||
|
// Play: https://go.dev/play/p/bAq9mHErB4V
|
||||||
|
func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) V {
|
||||||
|
if v, ok := in[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickBy returns same map type filtered by given predicate.
|
||||||
|
// Play: https://go.dev/play/p/kdg8GR_QMmf
|
||||||
|
func PickBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
for k, v := range in {
|
||||||
|
if predicate(k, v) {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickByKeys returns same map type filtered by given keys.
|
||||||
|
// Play: https://go.dev/play/p/R1imbuci9qU
|
||||||
|
func PickByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
for k, v := range in {
|
||||||
|
if Contains(keys, k) {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickByValues returns same map type filtered by given values.
|
||||||
|
// Play: https://go.dev/play/p/1zdzSvbfsJc
|
||||||
|
func PickByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
for k, v := range in {
|
||||||
|
if Contains(values, v) {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitBy returns same map type filtered by given predicate.
|
||||||
|
// Play: https://go.dev/play/p/EtBsR43bdsd
|
||||||
|
func OmitBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
for k, v := range in {
|
||||||
|
if !predicate(k, v) {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitByKeys returns same map type filtered by given keys.
|
||||||
|
// Play: https://go.dev/play/p/t1QjCrs-ysk
|
||||||
|
func OmitByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
for k, v := range in {
|
||||||
|
if !Contains(keys, k) {
|
||||||
|
r[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitByValues returns same map type filtered by given values.
|
||||||
|
// Play: https://go.dev/play/p/9UYZi-hrs8j
|
||||||
|
func OmitByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
|
||||||
|
r := map[K]V{}
|
||||||
|
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:
|
||||||
|
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
|
||||||
|
entries := make([]Entry[K, V], 0, len(in))
|
||||||
|
|
||||||
|
for k, v := range in {
|
||||||
|
entries = append(entries, Entry[K, V]{
|
||||||
|
Key: k,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPairs transforms a map into array 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.
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
for _, v := range entries {
|
||||||
|
out[v.Key] = v.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPairs transforms an array 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 {
|
||||||
|
return FromEntries(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert creates a map composed of the inverted keys and values. If map
|
||||||
|
// 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 {
|
||||||
|
out := make(map[V]K, len(in))
|
||||||
|
|
||||||
|
for k, v := range in {
|
||||||
|
out[v] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign merges multiple maps from left to right.
|
||||||
|
// Play: https://go.dev/play/p/VhwfJOyxf5o
|
||||||
|
func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||||
|
out := map[K]V{}
|
||||||
|
|
||||||
|
for _, m := range maps {
|
||||||
|
for k, v := range m {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapKeys manipulates a 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, v := range in {
|
||||||
|
result[iteratee(v, k)] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapValues manipulates a 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 {
|
||||||
|
result := make(map[K]R, len(in))
|
||||||
|
|
||||||
|
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.
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
for k1, v1 := range in {
|
||||||
|
k2, v2 := iteratee(k1, v1)
|
||||||
|
result[k2] = v2
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapToSlice transforms a map into a slice based on specific 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 {
|
||||||
|
result := make([]R, 0, len(in))
|
||||||
|
|
||||||
|
for k, v := range in {
|
||||||
|
result = append(result, iteratee(k, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import "golang.org/x/exp/constraints"
|
||||||
|
|
||||||
|
// Range creates an array 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)
|
||||||
|
result := make([]int, length)
|
||||||
|
step := If(elementNum < 0, -1).Else(1)
|
||||||
|
for i, j := 0, 0; i < length; i, j = i+1, j+step {
|
||||||
|
result[i] = j
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// RangeFrom creates an array 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)
|
||||||
|
result := make([]T, length)
|
||||||
|
step := If(elementNum < 0, -1).Else(1)
|
||||||
|
for i, j := 0, start; i < length; i, j = i+1, j+T(step) {
|
||||||
|
result[i] = j
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/0r6VimXAi9H
|
||||||
|
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
||||||
|
result := []T{}
|
||||||
|
if start == end || step == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if start < end {
|
||||||
|
if step < 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for i := start; i < end; i += step {
|
||||||
|
result = append(result, i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if step > 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for i := start; i > end; i += step {
|
||||||
|
result = append(result, i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
for _, val := range collection {
|
||||||
|
sum += val
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
for _, item := range collection {
|
||||||
|
sum = sum + iteratee(item)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type debounce struct {
|
||||||
|
after time.Duration
|
||||||
|
mu *sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
done bool
|
||||||
|
callbacks []func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *debounce) reset() {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if d.done {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.timer != nil {
|
||||||
|
d.timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
d.timer = time.AfterFunc(d.after, func() {
|
||||||
|
for _, f := range d.callbacks {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *debounce) cancel() {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if d.timer != nil {
|
||||||
|
d.timer.Stop()
|
||||||
|
d.timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDebounce creates a debounced instance that delays invoking functions given until after wait milliseconds have elapsed.
|
||||||
|
// Play: https://go.dev/play/p/mz32VMK2nqe
|
||||||
|
func NewDebounce(duration time.Duration, f ...func()) (func(), func()) {
|
||||||
|
d := &debounce{
|
||||||
|
after: duration,
|
||||||
|
mu: new(sync.Mutex),
|
||||||
|
timer: nil,
|
||||||
|
done: false,
|
||||||
|
callbacks: f,
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
d.reset()
|
||||||
|
}, d.cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
type debounceByItem struct {
|
||||||
|
mu *sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
type debounceBy[T comparable] struct {
|
||||||
|
after time.Duration
|
||||||
|
mu *sync.Mutex
|
||||||
|
items map[T]*debounceByItem
|
||||||
|
callbacks []func(key T, count int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *debounceBy[T]) reset(key T) {
|
||||||
|
d.mu.Lock()
|
||||||
|
if _, ok := d.items[key]; !ok {
|
||||||
|
d.items[key] = &debounceByItem{
|
||||||
|
mu: new(sync.Mutex),
|
||||||
|
timer: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item := d.items[key]
|
||||||
|
|
||||||
|
d.mu.Unlock()
|
||||||
|
|
||||||
|
item.mu.Lock()
|
||||||
|
defer item.mu.Unlock()
|
||||||
|
|
||||||
|
item.count++
|
||||||
|
|
||||||
|
if item.timer != nil {
|
||||||
|
item.timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
item.timer = time.AfterFunc(d.after, func() {
|
||||||
|
item.mu.Lock()
|
||||||
|
count := item.count
|
||||||
|
item.count = 0
|
||||||
|
item.mu.Unlock()
|
||||||
|
|
||||||
|
for _, f := range d.callbacks {
|
||||||
|
f(key, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *debounceBy[T]) cancel(key T) {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if item, ok := d.items[key]; ok {
|
||||||
|
item.mu.Lock()
|
||||||
|
|
||||||
|
if item.timer != nil {
|
||||||
|
item.timer.Stop()
|
||||||
|
item.timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
item.mu.Unlock()
|
||||||
|
|
||||||
|
delete(d.items, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDebounceBy creates a debounced instance for each distinct key, that delays invoking functions given until after wait milliseconds have elapsed.
|
||||||
|
// Play: https://go.dev/play/p/d3Vpt6pxhY8
|
||||||
|
func NewDebounceBy[T comparable](duration time.Duration, f ...func(key T, count int)) (func(key T), func(key T)) {
|
||||||
|
d := &debounceBy[T]{
|
||||||
|
after: duration,
|
||||||
|
mu: new(sync.Mutex),
|
||||||
|
items: map[T]*debounceByItem{},
|
||||||
|
callbacks: f,
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(key T) {
|
||||||
|
d.reset(key)
|
||||||
|
}, d.cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt invokes a function N times until it returns valid output. Returning either the caught error or nil. When first argument is less than `1`, the function runs until a successful response is returned.
|
||||||
|
// Play: https://go.dev/play/p/3ggJZ2ZKcMj
|
||||||
|
func Attempt(maxIteration int, f func(index int) error) (int, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||||
|
// for retries >= 0 {
|
||||||
|
err = f(i)
|
||||||
|
if err == nil {
|
||||||
|
return i + 1, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIteration, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttemptWithDelay invokes a function N times until it returns valid output,
|
||||||
|
// with a pause between each call. Returning either the caught error or nil.
|
||||||
|
// When first argument is less than `1`, the function runs until a successful
|
||||||
|
// response is returned.
|
||||||
|
// Play: https://go.dev/play/p/tVs6CygC7m1
|
||||||
|
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()
|
||||||
|
|
||||||
|
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||||
|
err = f(i, time.Since(start))
|
||||||
|
if err == nil {
|
||||||
|
return i + 1, time.Since(start), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxIteration <= 0 || i+1 < maxIteration {
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIteration, time.Since(start), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttemptWhile invokes a function N times until it returns valid output.
|
||||||
|
// Returning either the caught error or nil, and along with a bool value to identify
|
||||||
|
// whether it needs invoke function continuously. It will terminate the invoke
|
||||||
|
// immediately if second bool value is returned with falsy value. When first
|
||||||
|
// argument is less than `1`, the function runs until a successful response is
|
||||||
|
// returned.
|
||||||
|
func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) {
|
||||||
|
var err error
|
||||||
|
var shouldContinueInvoke bool
|
||||||
|
|
||||||
|
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||||
|
// for retries >= 0 {
|
||||||
|
err, shouldContinueInvoke = f(i)
|
||||||
|
if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately
|
||||||
|
return i + 1, err
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return i + 1, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIteration, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttemptWhileWithDelay invokes a function N times until it returns valid output,
|
||||||
|
// with a pause between each call. Returning either the caught error or nil, and along
|
||||||
|
// with a bool value to identify whether it needs to invoke function continuously.
|
||||||
|
// It will terminate the invoke immediately if second bool value is returned with falsy
|
||||||
|
// value. When first argument is less than `1`, the function runs until a successful
|
||||||
|
// response is returned.
|
||||||
|
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()
|
||||||
|
|
||||||
|
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
|
||||||
|
err, shouldContinueInvoke = f(i, time.Since(start))
|
||||||
|
if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately
|
||||||
|
return i + 1, time.Since(start), err
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return i + 1, time.Since(start), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxIteration <= 0 || i+1 < maxIteration {
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIteration, time.Since(start), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type transactionStep[T any] struct {
|
||||||
|
exec func(T) (T, error)
|
||||||
|
onRollback func(T) T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransaction instanciate a new transaction.
|
||||||
|
func NewTransaction[T any]() *Transaction[T] {
|
||||||
|
return &Transaction[T]{
|
||||||
|
steps: []transactionStep[T]{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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,
|
||||||
|
onRollback: onRollback,
|
||||||
|
})
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process runs the Transaction steps and rollbacks in case of errors.
|
||||||
|
func (t *Transaction[T]) Process(state T) (T, error) {
|
||||||
|
var i int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i < len(t.steps) {
|
||||||
|
state, err = t.steps[i].exec(state)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i > 0 {
|
||||||
|
i--
|
||||||
|
state = t.steps[i].onRollback(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// throttle ?
|
|
@ -0,0 +1,594 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for.
|
||||||
|
// Play: https://go.dev/play/p/Apjg3WeSi7K
|
||||||
|
func Filter[V any](collection []V, predicate func(item V, index int) bool) []V {
|
||||||
|
result := make([]V, 0, len(collection))
|
||||||
|
|
||||||
|
for i, item := range collection {
|
||||||
|
if predicate(item, i) {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
result := make([]R, len(collection))
|
||||||
|
|
||||||
|
for i, item := range collection {
|
||||||
|
result[i] = iteratee(item, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMap returns a slice which 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 {
|
||||||
|
result := []R{}
|
||||||
|
|
||||||
|
for i, item := range collection {
|
||||||
|
if r, ok := callback(item, i); ok {
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
result := make([]R, 0, len(collection))
|
||||||
|
|
||||||
|
for i, item := range collection {
|
||||||
|
result = append(result, iteratee(item, i)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
for i, item := range collection {
|
||||||
|
initial = accumulator(initial, item, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return initial
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceRight helper 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 {
|
||||||
|
for i := len(collection) - 1; i >= 0; i-- {
|
||||||
|
initial = accumulator(initial, collection[i], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return initial
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach iterates over elements of collection and invokes iteratee for each element.
|
||||||
|
// Play: https://go.dev/play/p/oofyiUPRf8t
|
||||||
|
func ForEach[T any](collection []T, iteratee func(item T, index int)) {
|
||||||
|
for i, item := range collection {
|
||||||
|
iteratee(item, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Times invokes the iteratee n times, returning an array 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 {
|
||||||
|
result := make([]T, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
result[i] = iteratee(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/DTzbeXZ6iEN
|
||||||
|
func Uniq[T comparable](collection []T) []T {
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
seen := make(map[T]struct{}, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if _, ok := seen[item]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[item] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/g42Z3QSb53u
|
||||||
|
func UniqBy[T any, U comparable](collection []T, iteratee func(item T) U) []T {
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
seen := make(map[U]struct{}, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee.
|
||||||
|
// Play: https://go.dev/play/p/XnQBd_v6brd
|
||||||
|
func GroupBy[T any, U comparable](collection []T, iteratee func(item T) U) map[U][]T {
|
||||||
|
result := map[U][]T{}
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
result[key] = append(result[key], item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/EeKl0AuTehH
|
||||||
|
func Chunk[T any](collection []T, size int) [][]T {
|
||||||
|
if size <= 0 {
|
||||||
|
panic("Second parameter must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
chunksNum := len(collection) / size
|
||||||
|
if len(collection)%size != 0 {
|
||||||
|
chunksNum += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([][]T, 0, chunksNum)
|
||||||
|
|
||||||
|
for i := 0; i < chunksNum; i++ {
|
||||||
|
last := (i + 1) * size
|
||||||
|
if last > len(collection) {
|
||||||
|
last = len(collection)
|
||||||
|
}
|
||||||
|
result = append(result, collection[i*size:last])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/NfQ_nGjkgXW
|
||||||
|
func PartitionBy[T any, K comparable](collection []T, iteratee func(item T) K) [][]T {
|
||||||
|
result := [][]T{}
|
||||||
|
seen := map[K]int{}
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
key := iteratee(item)
|
||||||
|
|
||||||
|
resultIndex, ok := seen[key]
|
||||||
|
if !ok {
|
||||||
|
resultIndex = len(result)
|
||||||
|
seen[key] = resultIndex
|
||||||
|
result = append(result, []T{})
|
||||||
|
}
|
||||||
|
|
||||||
|
result[resultIndex] = append(result[resultIndex], item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
// unordered:
|
||||||
|
// groups := GroupBy[T, K](collection, iteratee)
|
||||||
|
// return Values[K, []T](groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten returns an array a single level deep.
|
||||||
|
// Play: https://go.dev/play/p/rbp9ORaMpjw
|
||||||
|
func Flatten[T any](collection [][]T) []T {
|
||||||
|
totalLen := 0
|
||||||
|
for i := range collection {
|
||||||
|
totalLen += len(collection[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, totalLen)
|
||||||
|
for i := range collection {
|
||||||
|
result = append(result, collection[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave round-robin alternating input slices and sequentially appending value at index into result
|
||||||
|
// Play: https://go.dev/play/p/DDhlwrShbwe
|
||||||
|
func Interleave[T any](collections ...[]T) []T {
|
||||||
|
if len(collections) == 0 {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxSize := 0
|
||||||
|
totalSize := 0
|
||||||
|
for _, c := range collections {
|
||||||
|
size := len(c)
|
||||||
|
totalSize += size
|
||||||
|
if size > maxSize {
|
||||||
|
maxSize = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxSize == 0 {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, totalSize)
|
||||||
|
|
||||||
|
resultIdx := 0
|
||||||
|
for i := 0; i < maxSize; i++ {
|
||||||
|
for j := range collections {
|
||||||
|
if len(collections[j])-1 < i {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result[resultIdx] = collections[j][i]
|
||||||
|
resultIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
||||||
|
// Play: https://go.dev/play/p/Qp73bnTDnc7
|
||||||
|
func Shuffle[T any](collection []T) []T {
|
||||||
|
rand.Shuffle(len(collection), func(i, j int) {
|
||||||
|
collection[i], collection[j] = collection[j], collection[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
return collection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse reverses array 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/fhUMLvZ7vS6
|
||||||
|
func Reverse[T any](collection []T) []T {
|
||||||
|
length := len(collection)
|
||||||
|
half := length / 2
|
||||||
|
|
||||||
|
for i := 0; i < half; i = i + 1 {
|
||||||
|
j := length - 1 - i
|
||||||
|
collection[i], collection[j] = collection[j], collection[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill fills elements of array with `initial` value.
|
||||||
|
// Play: https://go.dev/play/p/VwR34GzqEub
|
||||||
|
func Fill[T Clonable[T]](collection []T, initial T) []T {
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
|
||||||
|
for range collection {
|
||||||
|
result = append(result, initial.Clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeat builds a slice with N copies of initial value.
|
||||||
|
// Play: https://go.dev/play/p/g3uHXbmc3b6
|
||||||
|
func Repeat[T Clonable[T]](count int, initial T) []T {
|
||||||
|
result := make([]T, 0, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
result = append(result, initial.Clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepeatBy builds a slice with values returned by N calls of callback.
|
||||||
|
// Play: https://go.dev/play/p/ozZLCtX_hNU
|
||||||
|
func RepeatBy[T any](count int, predicate func(index int) T) []T {
|
||||||
|
result := make([]T, 0, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
result = append(result, predicate(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V {
|
||||||
|
result := make(map[K]V, len(collection))
|
||||||
|
|
||||||
|
for _, v := range collection {
|
||||||
|
k := iteratee(v)
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// 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 {
|
||||||
|
result := make(map[K]V, len(collection))
|
||||||
|
|
||||||
|
for _, t := range collection {
|
||||||
|
k, v := transform(t)
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop drops n elements from the beginning of a slice or array.
|
||||||
|
// Play: https://go.dev/play/p/JswS7vXRJP2
|
||||||
|
func Drop[T any](collection []T, n int) []T {
|
||||||
|
if len(collection) <= n {
|
||||||
|
return make([]T, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-n)
|
||||||
|
|
||||||
|
return append(result, collection[n:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropRight drops n elements from the end of a slice or array.
|
||||||
|
// Play: https://go.dev/play/p/GG0nXkSJJa3
|
||||||
|
func DropRight[T any](collection []T, n int) []T {
|
||||||
|
if len(collection) <= n {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-n)
|
||||||
|
return append(result, collection[:len(collection)-n]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropWhile drops elements from the beginning of a slice or array while the predicate returns true.
|
||||||
|
// Play: https://go.dev/play/p/7gBPYw2IK16
|
||||||
|
func DropWhile[T any](collection []T, predicate func(item T) bool) []T {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(collection); i++ {
|
||||||
|
if !predicate(collection[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection)-i)
|
||||||
|
return append(result, collection[i:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropRightWhile drops elements from the end of a slice or array while the predicate returns true.
|
||||||
|
// Play: https://go.dev/play/p/3-n71oEC0Hz
|
||||||
|
func DropRightWhile[T any](collection []T, predicate func(item T) bool) []T {
|
||||||
|
i := len(collection) - 1
|
||||||
|
for ; i >= 0; i-- {
|
||||||
|
if !predicate(collection[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, i+1)
|
||||||
|
return append(result, collection[:i+1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func Reject[V any](collection []V, predicate func(item V, index int) bool) []V {
|
||||||
|
result := []V{}
|
||||||
|
|
||||||
|
for i, item := range collection {
|
||||||
|
if !predicate(item, i) {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count counts the number of elements in the collection that compare equal to value.
|
||||||
|
// Play: https://go.dev/play/p/Y3FlK54yveC
|
||||||
|
func Count[T comparable](collection []T, value T) (count int) {
|
||||||
|
for _, item := range collection {
|
||||||
|
if item == value {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
for _, item := range collection {
|
||||||
|
if predicate(item) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountValues counts the number of each element in the collection.
|
||||||
|
// Play: https://go.dev/play/p/-p-PyLT4dfy
|
||||||
|
func CountValues[T comparable](collection []T) map[T]int {
|
||||||
|
result := make(map[T]int)
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
result[item]++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountValuesBy counts the number of each element return 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 {
|
||||||
|
result := make(map[U]int)
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
result[mapper(item)]++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subset returns a copy of a slice from `offset` up to `length` elements. Like `slice[start:start+length]`, but does not panic on overflow.
|
||||||
|
// Play: https://go.dev/play/p/tOQu1GhFcog
|
||||||
|
func Subset[T any](collection []T, offset int, length uint) []T {
|
||||||
|
size := len(collection)
|
||||||
|
|
||||||
|
if offset < 0 {
|
||||||
|
offset = size + offset
|
||||||
|
if offset < 0 {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset > size {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > uint(size)-uint(offset) {
|
||||||
|
length = uint(size - offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection[offset : offset+int(length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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](collection []T, start int, end int) []T {
|
||||||
|
size := len(collection)
|
||||||
|
|
||||||
|
if start >= end {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start > size {
|
||||||
|
start = size
|
||||||
|
}
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > size {
|
||||||
|
end = size
|
||||||
|
}
|
||||||
|
if end < 0 {
|
||||||
|
end = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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](collection []T, old T, new T, n int) []T {
|
||||||
|
result := make([]T, len(collection))
|
||||||
|
copy(result, collection)
|
||||||
|
|
||||||
|
for i := range result {
|
||||||
|
if result[i] == old && n != 0 {
|
||||||
|
result[i] = new
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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](collection []T, old T, new T) []T {
|
||||||
|
return Replace(collection, old, new, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact returns a slice of all non-zero elements.
|
||||||
|
// Play: https://go.dev/play/p/tXiy-iK6PAc
|
||||||
|
func Compact[T comparable](collection []T) []T {
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
result := make([]T, 0, len(collection))
|
||||||
|
|
||||||
|
for _, item := range collection {
|
||||||
|
if item != zero {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSorted checks if a slice is sorted.
|
||||||
|
// Play: https://go.dev/play/p/mc3qR-t4mcx
|
||||||
|
func IsSorted[T constraints.Ordered](collection []T) bool {
|
||||||
|
for i := 1; i < len(collection); i++ {
|
||||||
|
if collection[i-1] > collection[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
for i := 0; i < size-1; i++ {
|
||||||
|
if iteratee(collection[i]) > iteratee(collection[i+1]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LowerCaseLettersCharset = []rune("abcdefghijklmnopqrstuvwxyz")
|
||||||
|
UpperCaseLettersCharset = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
LettersCharset = append(LowerCaseLettersCharset, UpperCaseLettersCharset...)
|
||||||
|
NumbersCharset = []rune("0123456789")
|
||||||
|
AlphanumericCharset = append(LettersCharset, NumbersCharset...)
|
||||||
|
SpecialCharset = []rune("!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
||||||
|
AllCharset = append(AlphanumericCharset, SpecialCharset...)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandomString return a random string.
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
if len(charset) <= 0 {
|
||||||
|
panic("lo.RandomString: Charset parameter must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]rune, size)
|
||||||
|
possibleCharactersCount := len(charset)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[rand.Intn(possibleCharactersCount)]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substring return part of a string.
|
||||||
|
// Play: https://go.dev/play/p/TQlxQi82Lu1
|
||||||
|
func Substring[T ~string](str T, offset int, length uint) T {
|
||||||
|
rs := []rune(str)
|
||||||
|
size := len(rs)
|
||||||
|
|
||||||
|
if offset < 0 {
|
||||||
|
offset = size + offset
|
||||||
|
if offset < 0 {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset > size {
|
||||||
|
return Empty[T]()
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > uint(size)-uint(offset) {
|
||||||
|
length = uint(size - offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return T(strings.Replace(string(rs[offset:offset+int(length)]), "\x00", "", -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return []T{""}
|
||||||
|
}
|
||||||
|
|
||||||
|
if size >= len(str) {
|
||||||
|
return []T{str}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunks []T = make([]T, 0, ((len(str)-1)/size)+1)
|
||||||
|
currentLen := 0
|
||||||
|
currentStart := 0
|
||||||
|
for i := range str {
|
||||||
|
if currentLen == size {
|
||||||
|
chunks = append(chunks, str[currentStart:i])
|
||||||
|
currentLen = 0
|
||||||
|
currentStart = i
|
||||||
|
}
|
||||||
|
currentLen++
|
||||||
|
}
|
||||||
|
chunks = append(chunks, str[currentStart:])
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneLength is an alias to utf8.RuneCountInString which returns the number of runes in string.
|
||||||
|
// Play: https://go.dev/play/p/tuhgW_lWY8l
|
||||||
|
func RuneLength(str string) int {
|
||||||
|
return utf8.RuneCountInString(str)
|
||||||
|
}
|
|
@ -0,0 +1,513 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// T2 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T2[A any, B any](a A, b B) Tuple2[A, B] {
|
||||||
|
return Tuple2[A, B]{A: a, B: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T3 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T3[A any, B any, C any](a A, b B, c C) Tuple3[A, B, C] {
|
||||||
|
return Tuple3[A, B, C]{A: a, B: b, C: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T4 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T4[A any, B any, C any, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] {
|
||||||
|
return Tuple4[A, B, C, D]{A: a, B: b, C: c, D: d}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T5 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T5[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] {
|
||||||
|
return Tuple5[A, B, C, D, E]{A: a, B: b, C: c, D: d, E: e}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T6 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T6[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] {
|
||||||
|
return Tuple6[A, B, C, D, E, F]{A: a, B: b, C: c, D: d, E: e, F: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T7 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T7[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] {
|
||||||
|
return Tuple7[A, B, C, D, E, F, G]{A: a, B: b, C: c, D: d, E: e, F: f, G: g}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T8 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T8[A any, B any, C any, D any, E any, F any, G any, 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] {
|
||||||
|
return Tuple8[A, B, C, D, E, F, G, H]{A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
// T9 creates a tuple from a list of values.
|
||||||
|
// Play: https://go.dev/play/p/IllL3ZO4BQm
|
||||||
|
func T9[A any, B any, C any, D any, E any, F any, G any, H any, 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] {
|
||||||
|
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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack2[A any, B any](tuple Tuple2[A, B]) (A, B) {
|
||||||
|
return tuple.A, tuple.B
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack3 returns values contained in tuple.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack3[A any, B any, C any](tuple Tuple3[A, B, C]) (A, B, C) {
|
||||||
|
return tuple.A, tuple.B, tuple.C
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack4 returns values contained in tuple.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack4[A any, B any, C any, 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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack5[A any, B any, C any, D any, 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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack6[A any, B any, C any, D any, E any, 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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack7[A any, B any, C any, D any, E any, F any, 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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack8[A any, B any, C any, D any, E any, F any, G any, 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.
|
||||||
|
// Play: https://go.dev/play/p/xVP_k0kJ96W
|
||||||
|
func Unpack9[A any, B any, C any, D any, E any, F any, G any, H any, 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip2[A any, B any](a []A, b []B) []Tuple2[A, B] {
|
||||||
|
size := Max([]int{len(a), len(b)})
|
||||||
|
|
||||||
|
result := make([]Tuple2[A, B], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple2[A, B]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip3[A any, B any, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
|
||||||
|
size := Max([]int{len(a), len(b), len(c)})
|
||||||
|
|
||||||
|
result := make([]Tuple3[A, B, C], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple3[A, B, C]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip4[A any, B any, C any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple4[A, B, C, D], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple4[A, B, C, D]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
D: _d,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip5[A any, B any, C any, D any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple5[A, B, C, D, E], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
_e, _ := Nth(e, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple5[A, B, C, D, E]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
D: _d,
|
||||||
|
E: _e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip6[A any, B any, C any, D any, E any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple6[A, B, C, D, E, F], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
_e, _ := Nth(e, index)
|
||||||
|
_f, _ := Nth(f, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple6[A, B, C, D, E, F]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
D: _d,
|
||||||
|
E: _e,
|
||||||
|
F: _f,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip7[A any, B any, C any, D any, E any, F any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple7[A, B, C, D, E, F, G], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
_e, _ := Nth(e, index)
|
||||||
|
_f, _ := Nth(f, index)
|
||||||
|
_g, _ := Nth(g, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple7[A, B, C, D, E, F, G]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
D: _d,
|
||||||
|
E: _e,
|
||||||
|
F: _f,
|
||||||
|
G: _g,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip8[A any, B any, C any, D any, E any, F any, G any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple8[A, B, C, D, E, F, G, H], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
_e, _ := Nth(e, index)
|
||||||
|
_f, _ := Nth(f, index)
|
||||||
|
_g, _ := Nth(g, index)
|
||||||
|
_h, _ := Nth(h, index)
|
||||||
|
|
||||||
|
result = append(result, Tuple8[A, B, C, D, E, F, G, H]{
|
||||||
|
A: _a,
|
||||||
|
B: _b,
|
||||||
|
C: _c,
|
||||||
|
D: _d,
|
||||||
|
E: _e,
|
||||||
|
F: _f,
|
||||||
|
G: _g,
|
||||||
|
H: _h,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Play: https://go.dev/play/p/jujaA6GaJTp
|
||||||
|
func Zip9[A any, B any, C any, D any, E any, F any, G any, H any, 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)})
|
||||||
|
|
||||||
|
result := make([]Tuple9[A, B, C, D, E, F, G, H, I], 0, size)
|
||||||
|
|
||||||
|
for index := 0; index < size; index++ {
|
||||||
|
_a, _ := Nth(a, index)
|
||||||
|
_b, _ := Nth(b, index)
|
||||||
|
_c, _ := Nth(c, index)
|
||||||
|
_d, _ := Nth(d, index)
|
||||||
|
_e, _ := Nth(e, index)
|
||||||
|
_f, _ := Nth(f, index)
|
||||||
|
_g, _ := Nth(g, index)
|
||||||
|
_h, _ := Nth(h, index)
|
||||||
|
_i, _ := Nth(i, index)
|
||||||
|
|
||||||
|
result = append(result, 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip2 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip2[A any, B any](tuples []Tuple2[A, B]) ([]A, []B) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip3 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip3[A any, B any, C any](tuples []Tuple3[A, B, C]) ([]A, []B, []C) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip4 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip4[A any, B any, C any, D any](tuples []Tuple4[A, B, C, D]) ([]A, []B, []C, []D) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip5 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip5[A any, B any, C any, D any, E any](tuples []Tuple5[A, B, C, D, E]) ([]A, []B, []C, []D, []E) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
r5 := make([]E, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
r5 = append(r5, tuple.E)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4, r5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip6 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip6[A any, B any, C any, D any, E any, F any](tuples []Tuple6[A, B, C, D, E, F]) ([]A, []B, []C, []D, []E, []F) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
r5 := make([]E, 0, size)
|
||||||
|
r6 := make([]F, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
r5 = append(r5, tuple.E)
|
||||||
|
r6 = append(r6, tuple.F)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4, r5, r6
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip7 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip7[A any, B any, C any, D any, E any, F any, G any](tuples []Tuple7[A, B, C, D, E, F, G]) ([]A, []B, []C, []D, []E, []F, []G) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
r5 := make([]E, 0, size)
|
||||||
|
r6 := make([]F, 0, size)
|
||||||
|
r7 := make([]G, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
r5 = append(r5, tuple.E)
|
||||||
|
r6 = append(r6, tuple.F)
|
||||||
|
r7 = append(r7, tuple.G)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4, r5, r6, r7
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip8 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip8[A any, B any, C any, D any, E any, F any, G any, H any](tuples []Tuple8[A, B, C, D, E, F, G, H]) ([]A, []B, []C, []D, []E, []F, []G, []H) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
r5 := make([]E, 0, size)
|
||||||
|
r6 := make([]F, 0, size)
|
||||||
|
r7 := make([]G, 0, size)
|
||||||
|
r8 := make([]H, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
r5 = append(r5, tuple.E)
|
||||||
|
r6 = append(r6, tuple.F)
|
||||||
|
r7 = append(r7, tuple.G)
|
||||||
|
r8 = append(r8, tuple.H)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4, r5, r6, r7, r8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip9 accepts an array of grouped elements and creates an array regrouping the elements
|
||||||
|
// to their pre-zip configuration.
|
||||||
|
// Play: https://go.dev/play/p/ciHugugvaAW
|
||||||
|
func Unzip9[A any, B any, C any, D any, E any, F any, G any, H any, I any](tuples []Tuple9[A, B, C, D, E, F, G, H, I]) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) {
|
||||||
|
size := len(tuples)
|
||||||
|
r1 := make([]A, 0, size)
|
||||||
|
r2 := make([]B, 0, size)
|
||||||
|
r3 := make([]C, 0, size)
|
||||||
|
r4 := make([]D, 0, size)
|
||||||
|
r5 := make([]E, 0, size)
|
||||||
|
r6 := make([]F, 0, size)
|
||||||
|
r7 := make([]G, 0, size)
|
||||||
|
r8 := make([]H, 0, size)
|
||||||
|
r9 := make([]I, 0, size)
|
||||||
|
|
||||||
|
for _, tuple := range tuples {
|
||||||
|
r1 = append(r1, tuple.A)
|
||||||
|
r2 = append(r2, tuple.B)
|
||||||
|
r3 = append(r3, tuple.C)
|
||||||
|
r4 = append(r4, tuple.D)
|
||||||
|
r5 = append(r5, tuple.E)
|
||||||
|
r6 = append(r6, tuple.F)
|
||||||
|
r7 = append(r7, tuple.G)
|
||||||
|
r8 = append(r8, tuple.H)
|
||||||
|
r9 = append(r9, tuple.I)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, r2, r3, r4, r5, r6, r7, r8, r9
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// ToPtr returns a pointer copy of value.
|
||||||
|
func ToPtr[T any](x T) *T {
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil checks if a value is nil or if it's a reference type with a nil underlying value.
|
||||||
|
func IsNil(x any) bool {
|
||||||
|
defer func() { recover() }()
|
||||||
|
return x == nil || reflect.ValueOf(x).IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmptyableToPtr returns a pointer copy of value if it's nonzero.
|
||||||
|
// Otherwise, returns nil pointer.
|
||||||
|
func EmptyableToPtr[T any](x T) *T {
|
||||||
|
// 🤮
|
||||||
|
isZero := reflect.ValueOf(&x).Elem().IsZero()
|
||||||
|
if isZero {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPtr returns the pointer value or empty.
|
||||||
|
func FromPtr[T any](x *T) T {
|
||||||
|
if x == nil {
|
||||||
|
return Empty[T]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPtrOr returns the pointer value or the fallback value.
|
||||||
|
func FromPtrOr[T any](x *T, fallback T) T {
|
||||||
|
if x == nil {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlicePtr returns a slice of pointer copy of value.
|
||||||
|
func ToSlicePtr[T any](collection []T) []*T {
|
||||||
|
return Map(collection, func(x T, _ int) *T {
|
||||||
|
return &x
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAnySlice returns a slice with all elements mapped to `any` type
|
||||||
|
func ToAnySlice[T any](collection []T) []any {
|
||||||
|
result := make([]any, len(collection))
|
||||||
|
for i, item := range collection {
|
||||||
|
result[i] = item
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromAnySlice returns an `any` 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))
|
||||||
|
for i, item := range in {
|
||||||
|
result[i] = item.(T)
|
||||||
|
}
|
||||||
|
return result, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns an empty value.
|
||||||
|
func Empty[T any]() T {
|
||||||
|
var zero T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if argument is a zero value.
|
||||||
|
func IsEmpty[T comparable](v T) bool {
|
||||||
|
var zero T
|
||||||
|
return zero == v
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotEmpty returns true if argument is not a zero value.
|
||||||
|
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](v ...T) (result T, ok bool) {
|
||||||
|
for _, e := range v {
|
||||||
|
if e != result {
|
||||||
|
result = e
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package lo
|
||||||
|
|
||||||
|
// Entry defines a key/value pairs.
|
||||||
|
type Entry[K comparable, V any] struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple2 is a group of 2 elements (pair).
|
||||||
|
type Tuple2[A any, B any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
func (t Tuple2[A, B]) Unpack() (A, B) {
|
||||||
|
return t.A, t.B
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple3 is a group of 3 elements.
|
||||||
|
type Tuple3[A any, B any, C any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
func (t Tuple3[A, B, C]) Unpack() (A, B, C) {
|
||||||
|
return t.A, t.B, t.C
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple4 is a group of 4 elements.
|
||||||
|
type Tuple4[A any, B any, C any, D any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
func (t Tuple4[A, B, C, D]) Unpack() (A, B, C, D) {
|
||||||
|
return t.A, t.B, t.C, t.D
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple5 is a group of 5 elements.
|
||||||
|
type Tuple5[A any, B any, C any, D any, E any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
E E
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple6 is a group of 6 elements.
|
||||||
|
type Tuple6[A any, B any, C any, D any, E any, F any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
E E
|
||||||
|
F F
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple7 is a group of 7 elements.
|
||||||
|
type Tuple7[A any, B any, C any, D any, E any, F any, G any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
E E
|
||||||
|
F F
|
||||||
|
G G
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple8 is a group of 8 elements.
|
||||||
|
type Tuple8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
E E
|
||||||
|
F F
|
||||||
|
G G
|
||||||
|
H H
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tuple9 is a group of 9 elements.
|
||||||
|
type Tuple9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct {
|
||||||
|
A A
|
||||||
|
B B
|
||||||
|
C C
|
||||||
|
D D
|
||||||
|
E E
|
||||||
|
F F
|
||||||
|
G G
|
||||||
|
H H
|
||||||
|
I I
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack returns values contained in tuple.
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package constraints defines a set of useful constraints to be used
|
||||||
|
// with type parameters.
|
||||||
|
package constraints
|
||||||
|
|
||||||
|
// Signed is a constraint that permits any signed integer type.
|
||||||
|
// If future releases of Go add new predeclared signed integer types,
|
||||||
|
// this constraint will be modified to include them.
|
||||||
|
type Signed interface {
|
||||||
|
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsigned is a constraint that permits any unsigned integer type.
|
||||||
|
// If future releases of Go add new predeclared unsigned integer types,
|
||||||
|
// this constraint will be modified to include them.
|
||||||
|
type Unsigned interface {
|
||||||
|
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer is a constraint that permits any integer type.
|
||||||
|
// If future releases of Go add new predeclared integer types,
|
||||||
|
// this constraint will be modified to include them.
|
||||||
|
type Integer interface {
|
||||||
|
Signed | Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float is a constraint that permits any floating-point type.
|
||||||
|
// If future releases of Go add new predeclared floating-point types,
|
||||||
|
// this constraint will be modified to include them.
|
||||||
|
type Float interface {
|
||||||
|
~float32 | ~float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complex is a constraint that permits any complex numeric type.
|
||||||
|
// If future releases of Go add new predeclared complex numeric types,
|
||||||
|
// this constraint will be modified to include them.
|
||||||
|
type Complex interface {
|
||||||
|
~complex64 | ~complex128
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 interface {
|
||||||
|
Integer | Float | ~string
|
||||||
|
}
|
|
@ -1,3 +1,9 @@
|
||||||
# git.pyer.club/kingecg/gologger v1.0.0
|
# git.pyer.club/kingecg/gologger v1.0.0
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
git.pyer.club/kingecg/gologger
|
git.pyer.club/kingecg/gologger
|
||||||
|
# github.com/samber/lo v1.39.0
|
||||||
|
## explicit; go 1.18
|
||||||
|
github.com/samber/lo
|
||||||
|
# golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17
|
||||||
|
## explicit; go 1.18
|
||||||
|
golang.org/x/exp/constraints
|
||||||
|
|
Loading…
Reference in New Issue