doc: adding go playground examples

This commit is contained in:
Samuel Berthe
2025-09-23 14:54:30 +02:00
parent 9c8308ffda
commit 3bc887c57c
18 changed files with 249 additions and 17 deletions

View File

@@ -392,6 +392,8 @@ lop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string {
// []string{"1", "2", "3", "4"}
```
[[play](https://go.dev/play/p/sCJaB3quRMC)]
Mutable: like `lo.Map()`, but the slice is updated in place.
```go
@@ -404,6 +406,8 @@ lom.Map(list, func(x int) int {
// []int{2, 4, 6, 8}
```
[[play](https://go.dev/play/p/0jY3Z0B7O_5)]
### UniqMap
Manipulates a slice and transforms it to a slice of another type with unique values.
@@ -421,6 +425,8 @@ names := lo.UniqMap(users, func(u User, index int) string {
// []string{"Alex", "Bob", "Alice"}
```
[[play](https://go.dev/play/p/fygzLBhvUdB)]
### FilterMap
Returns a slice which obtained after both filtering and mapping using the given callback function.
@@ -616,6 +622,8 @@ groups := lo.GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int) {
// map[int][]int{0: []int{0, 6}, 1: []int{2, 8}, 2: []int{4, 10}}
```
[[play](https://go.dev/play/p/iMeruQ3_W80)]
### Chunk
Returns an array of elements split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements.
@@ -634,7 +642,7 @@ lo.Chunk([]int{0}, 2)
// [][]int{{0}}
```
[[play](https://go.dev/play/p/EeKl0AuTehH)]
[[play](https://go.dev/play/p/kEMkFbdu85g)]
### PartitionBy
@@ -853,6 +861,8 @@ result := lo.FilterSliceToMap(list, func(str string) (string, int, bool) {
// map[string][int]{"aa":2 "aaa":3}
```
[[play](https://go.dev/play/p/2z0rDz2ZSGU)]
### Keyify
Returns a map with each unique element of the slice as a key.
@@ -862,6 +872,8 @@ set := lo.Keyify([]int{1, 1, 2, 3, 4})
// map[int]struct{}{1:{}, 2:{}, 3:{}, 4:{}}
```
[[play](https://go.dev/play/p/RYhhM_csqIG)]
### Drop
Drops n elements from the beginning of a slice or array.
@@ -1360,7 +1372,7 @@ entries := lo.Entries(map[string]int{"foo": 1, "bar": 2})
// }
```
[[play](https://go.dev/play/p/3Dhgx46gawJ)]
[[play](https://go.dev/play/p/_t4Xe34-Nl5)]
### FromEntries (alias: FromPairs)
@@ -1678,6 +1690,8 @@ mean := lo.MeanBy([]float64{}, mapper)
// 0
```
[[play](https://go.dev/play/p/j7TsVwBOZ7P)]
### Mode
@@ -1773,7 +1787,7 @@ str := lo.PascalCase("hello_world")
// HelloWorld
```
[[play](https://go.dev/play/p/iZkdeLP9oiB)]
[[play](https://go.dev/play/p/Dy_V_6DUYhe)]
### CamelCase
@@ -1784,7 +1798,7 @@ str := lo.CamelCase("hello_world")
// helloWorld
```
[[play](https://go.dev/play/p/dtyFB58MBRp)]
[[play](https://go.dev/play/p/Go6aKwUiq59)]
### KebabCase
@@ -1795,7 +1809,7 @@ str := lo.KebabCase("helloWorld")
// hello-world
```
[[play](https://go.dev/play/p/2YTuPafwECA)]
[[play](https://go.dev/play/p/96gT_WZnTVP)]
### SnakeCase
@@ -1806,7 +1820,7 @@ str := lo.SnakeCase("HelloWorld")
// hello_world
```
[[play](https://go.dev/play/p/QVKJG9nOnDg)]
[[play](https://go.dev/play/p/ziB0V89IeVH)]
### Words
@@ -1817,7 +1831,7 @@ str := lo.Words("helloWorld")
// []string{"hello", "world"}
```
[[play](https://go.dev/play/p/2P4zhqqq61g)]
[[play](https://go.dev/play/p/-f3VIQqiaVw)]
### Capitalize
@@ -1828,6 +1842,8 @@ str := lo.Capitalize("heLLO")
// Hello
```
[[play](https://go.dev/play/p/uLTZZQXqnsa)]
### Ellipsis
Trims and truncates a string to a specified length in `bytes` and appends an ellipsis if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8), truncating by byte length may split a character in the middle, potentially resulting in garbled output.
@@ -1843,6 +1859,8 @@ str := lo.Ellipsis("Lorem Ipsum", 3)
// ...
```
[[play](https://go.dev/play/p/qE93rgqe1TW)]
### T2 -> T9
Creates a tuple from a list of values.
@@ -1968,6 +1986,8 @@ duration := lo.Duration(func() {
// 3s
```
[[play](https://go.dev/play/p/HQfbBbAXaFP)]
### Duration0 -> Duration10
Returns the time taken to execute a function.
@@ -2028,6 +2048,8 @@ for i := range children {
}
```
[[play](https://go.dev/play/p/UZGu2wVg3J2)]
Many distributions strategies are available:
- [lo.DispatchingStrategyRoundRobin](./channel.go): Distributes messages in a rotating sequential manner.
@@ -2088,6 +2110,8 @@ for v := range lo.SliceToChannel(2, list) {
// prints 1, then 2, then 3, then 4, then 5
```
[[play](https://go.dev/play/p/lIbSY3QmiEg)]
### ChannelToSlice
Returns a slice built from channels items. Blocks until channel closes.
@@ -2274,6 +2298,8 @@ present := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5)
// true
```
[[play](https://go.dev/play/p/W1EvyqY6t9j)]
### ContainsBy
Returns true if the predicate function returns `true`.
@@ -2308,6 +2334,8 @@ b := EveryBy([]int{1, 2, 3, 4}, func(x int) bool {
// true
```
[[play](https://go.dev/play/p/dn1-vhHsq9x)]
### Some
Returns true if at least 1 element of a subset is contained into a collection.
@@ -2316,6 +2344,9 @@ If the subset is empty Some returns false.
```go
ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// true
```
[[play](https://go.dev/play/p/Lj4ceFkeT9V)]
ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// false
@@ -2344,6 +2375,8 @@ b := None([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// true
```
[[play](https://go.dev/play/p/fye7JsmxzPV)]
### NoneBy
Returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
@@ -2355,6 +2388,8 @@ b := NoneBy([]int{1, 2, 3, 4}, func(x int) bool {
// true
```
[[play](https://go.dev/play/p/O64WZ32H58S)]
### Intersect
Returns the intersection between two collections.
@@ -2385,6 +2420,8 @@ left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5})
// []int{}, []int{}
```
[[play](https://go.dev/play/p/pKE-JgzqRpz)]
### Union
Returns all distinct elements from given collections. Result will not change the order of elements relatively.
@@ -2499,6 +2536,8 @@ notFound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6)
// -1
```
[[play](https://go.dev/play/p/Eo7W0lvKTky)]
### LastIndexOf
Returns the index at which the last occurrence of a value is found in an array or return -1 if the value cannot be found.
@@ -2527,6 +2566,8 @@ str, ok := lo.Find([]string{"foobar"}, func(i string) bool {
// "", false
```
[[play](https://go.dev/play/p/Eo7W0lvKTky)]
### FindIndexOf
FindIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found.
@@ -2543,6 +2584,8 @@ str, index, ok := lo.FindIndexOf([]string{"foobar"}, func(i string) bool {
// "", -1, false
```
[[play](https://go.dev/play/p/XWSEM4Ic_t0)]
### FindLastIndexOf
FindLastIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found.
@@ -2559,6 +2602,8 @@ str, index, ok := lo.FindLastIndexOf([]string{"foobar"}, func(i string) bool {
// "", -1, false
```
[[play](https://go.dev/play/p/dPiMRtJ6cUx)]
### FindOrElse
Search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.
@@ -2666,6 +2711,8 @@ min := lo.Min([]time.Duration{time.Second, time.Hour})
// 1s
```
[[play](https://go.dev/play/p/r6e-Z8JozS8)]
### MinIndex
Search the minimum value of a collection and the index of the minimum value.
@@ -2985,6 +3032,8 @@ lo.Sample([]string{})
// ""
```
[[play](https://go.dev/play/p/FYA45LcpfM2)]
### SampleBy
@@ -3215,6 +3264,8 @@ ptr := lo.ToPtr("hello world")
// *string{"hello world"}
```
[[play](https://go.dev/play/p/P2sD0PMXw4F)]
### Nil
Returns a nil pointer of type.
@@ -3503,6 +3554,8 @@ f(42)
// 47
```
[[play](https://go.dev/play/p/Sy1gAQiQZ3v)]
### Partial2 -> Partial5
Returns new function that, when called, has its first argument set to the provided value.
@@ -3518,6 +3571,8 @@ f(42, -4)
// 80
```
[[play](https://go.dev/play/p/-xiPjy4JChJ)]
### Attempt
Invokes a function N times until it returns valid output. Returns either the caught error or nil.
@@ -3606,7 +3661,7 @@ count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) {
For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).
[[play](https://go.dev/play/p/M2wVq24PaZM)]
[[play](https://go.dev/play/p/1VS7HxlYMOG)]
### AttemptWhileWithDelay
@@ -3631,7 +3686,7 @@ count1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int,
For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).
[[play](https://go.dev/play/p/cfcmhvLO-nv)]
[[play](https://go.dev/play/p/mhufUjJfLEF)]
### Debounce
@@ -3876,6 +3931,8 @@ iterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, 5*time.Mi
// false
```
[[play](https://go.dev/play/p/t_wTDmubbK3)]
### WaitForWithContext
Runs periodically until a condition is validated or context is invalid.
@@ -3918,6 +3975,8 @@ iterations, duration, ok := lo.WaitForWithContext(expiringCtx, alwaysFalse, 100*
// false
```
[[play](https://go.dev/play/p/t_wTDmubbK3)]
### Validate
Helper function that creates an error when a condition is not met.

View File

@@ -14,6 +14,7 @@ type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) i
// 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.
// Play: https://go.dev/play/p/UZGu2wVg3J2
func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T {
children := createChannels[T](count, channelBufferCap)
@@ -73,6 +74,7 @@ func channelIsNotFull[T any](ch <-chan T) bool {
// DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner.
// If the channel capacity is exceeded, the next channel will be selected and so on.
// Play: https://go.dev/play/p/UZGu2wVg3J2
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int {
for {
i := int(index % uint64(len(channels)))
@@ -87,6 +89,7 @@ func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan
// DispatchingStrategyRandom distributes messages in a random manner.
// If the channel capacity is exceeded, another random channel will be selected and so on.
// Play: https://go.dev/play/p/GEyGn3TdGk4
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int {
for {
i := rand.IntN(len(channels))
@@ -100,6 +103,7 @@ func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T)
// DispatchingStrategyWeightedRandom distributes messages in a weighted manner.
// If the channel capacity is exceeded, another random channel will be selected and so on.
// Play: https://go.dev/play/p/v0eMh8NZG2L
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
seq := []int{}
@@ -123,6 +127,7 @@ func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy
// DispatchingStrategyFirst distributes messages in the first non-full channel.
// If the capacity of the first channel is exceeded, the second channel will be selected and so on.
// Play: https://go.dev/play/p/OrJCvOmk42f
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int {
for {
for i := range channels {
@@ -136,6 +141,7 @@ func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) i
}
// DispatchingStrategyLeast distributes messages in the emptiest channel.
// Play: https://go.dev/play/p/ypy0jrRcEe7
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int {
seq := Range(len(channels))
@@ -146,6 +152,7 @@ func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) i
// DispatchingStrategyMost distributes messages in the fullest channel.
// If the channel capacity is exceeded, the next channel will be selected and so on.
// Play: https://go.dev/play/p/erHHone7rF9
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int {
seq := Range(len(channels))
@@ -155,6 +162,7 @@ func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) in
}
// SliceToChannel returns a read-only channels of collection elements.
// Play: https://go.dev/play/p/lIbSY3QmiEg
func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
ch := make(chan T, bufferSize)
@@ -170,6 +178,7 @@ func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
}
// ChannelToSlice returns a slice built from channels items. Blocks until channel closes.
// Play: https://go.dev/play/p/lIbSY3QmiEg
func ChannelToSlice[T any](ch <-chan T) []T {
collection := []T{}
@@ -181,6 +190,7 @@ func ChannelToSlice[T any](ch <-chan T) []T {
}
// Generator implements the generator design pattern.
// Play: https://go.dev/play/p/lIbSY3QmiEg
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
ch := make(chan T, bufferSize)
@@ -198,6 +208,7 @@ func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
// Buffer creates a slice of n elements from a channel. Returns the slice and the slice length.
// @TODO: we should probably provide an helper that reuse the same buffer.
// Play: https://go.dev/play/p/gPQ-6xmcKQI
func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
buffer := make([]T, 0, size)
index := 0
@@ -224,6 +235,7 @@ func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime t
// BufferWithContext creates a slice of n elements from a channel, with context. Returns the slice and the slice length.
// @TODO: we should probably provide an helper that reuse the same buffer.
// Play: https://go.dev/play/p/oRfOyJWK9YF
func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
buffer := make([]T, 0, size)
now := time.Now()
@@ -246,6 +258,7 @@ func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (colle
}
// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
// Play: https://go.dev/play/p/sxyEM3koo4n
func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
@@ -261,6 +274,7 @@ func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (coll
// FanIn collects messages from multiple input channels into a single buffered channel.
// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes.
// Play: https://go.dev/play/p/FH8Wq-T04Jb
func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
out := make(chan T, channelBufferCap)
var wg sync.WaitGroup
@@ -295,6 +309,7 @@ func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
// FanOut broadcasts all the upstream messages to multiple downstream channels.
// When upstream channel reach EOF, downstream channels close. If any downstream
// channels is full, broadcasting is paused.
// Play: https://go.dev/play/p/2LHxcjKX23L
func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T {
downstreams := createChannels[T](count, channelsBufferCap)

View File

@@ -17,6 +17,7 @@ func (s *synchronize) Do(cb func()) {
}
// Synchronize wraps the underlying callback in a mutex. It receives an optional mutex.
// Play: https://go.dev/play/p/X3cqROSpQmu
func Synchronize(opt ...sync.Locker) *synchronize { //nolint:revive
if len(opt) > 1 {
panic("unexpected arguments")
@@ -30,6 +31,7 @@ func Synchronize(opt ...sync.Locker) *synchronize { //nolint:revive
}
// Async executes a function in a goroutine and returns the result in a channel.
// Play: https://go.dev/play/p/uo35gosuTLw
func Async[A any](f func() A) <-chan A {
ch := make(chan A, 1)
go func() {
@@ -39,6 +41,7 @@ func Async[A any](f func() A) <-chan A {
}
// Async0 executes a function in a goroutine and returns a channel set once the function finishes.
// Play: https://go.dev/play/p/tNqf1cClG_o
func Async0(f func()) <-chan struct{} {
ch := make(chan struct{}, 1)
go func() {
@@ -49,11 +52,13 @@ func Async0(f func()) <-chan struct{} {
}
// Async1 is an alias to Async.
// Play: https://go.dev/play/p/uo35gosuTLw
func Async1[A any](f func() A) <-chan A {
return Async(f)
}
// Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel.
// Play: https://go.dev/play/p/7W7mKQi0AhA
func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
ch := make(chan Tuple2[A, B], 1)
go func() {
@@ -63,6 +68,7 @@ func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
}
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
// Play: https://go.dev/play/p/L1d6o6l6q0d
func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
ch := make(chan Tuple3[A, B, C], 1)
go func() {
@@ -72,6 +78,7 @@ func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
}
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
// Play: https://go.dev/play/p/1X7q6oL0TqF
func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
ch := make(chan Tuple4[A, B, C, D], 1)
go func() {
@@ -81,6 +88,7 @@ func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
}
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
// Play: https://go.dev/play/p/2W7q4oL1TqG
func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C, D, E] {
ch := make(chan Tuple5[A, B, C, D, E], 1)
go func() {
@@ -90,6 +98,7 @@ func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C,
}
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
// Play: https://go.dev/play/p/3X8q5pM2UrH
func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, B, C, D, E, F] {
ch := make(chan Tuple6[A, B, C, D, E, F], 1)
go func() {
@@ -99,6 +108,7 @@ func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A,
}
// WaitFor runs periodically until a condition is validated.
// Play: https://go.dev/play/p/t_wTDmubbK3
func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
conditionWithContext := func(_ context.Context, currentIteration int) bool {
return condition(currentIteration)
@@ -107,6 +117,7 @@ func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay t
}
// WaitForWithContext runs periodically until a condition is validated or context is canceled.
// Play: https://go.dev/play/p/t_wTDmubbK3
func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
start := time.Now()

View File

@@ -61,7 +61,7 @@ func must(err any, messageArgs ...any) {
// Must is a helper that wraps a call to a function returning a value and an error
// and panics if err is error or false.
// Play: https://go.dev/play/p/TMoWrRp3DyC
// Play: https://go.dev/play/p/fOqtX5HudtN
func Must[T any](val T, err any, messageArgs ...any) T {
must(err, messageArgs...)
return val

23
find.go
View File

@@ -10,6 +10,7 @@ import (
// 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.
// Play: https://go.dev/play/p/Eo7W0lvKTky
func IndexOf[T comparable](collection []T, element T) int {
for i := range collection {
if collection[i] == element {
@@ -22,6 +23,7 @@ func IndexOf[T comparable](collection []T, element T) int {
// 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.
// Play: https://go.dev/play/p/Eo7W0lvKTky
func LastIndexOf[T comparable](collection []T, element T) int {
length := len(collection)
@@ -35,6 +37,7 @@ func LastIndexOf[T comparable](collection []T, element T) int {
}
// Find search an element in a slice based on a predicate. It returns element and true if element was found.
// Play: https://go.dev/play/p/Eo7W0lvKTky
func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
for i := range collection {
if predicate(collection[i]) {
@@ -48,6 +51,7 @@ func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
// 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.
// Play: https://go.dev/play/p/XWSEM4Ic_t0
func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
for i := range collection {
if predicate(collection[i]) {
@@ -61,6 +65,7 @@ func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bo
// 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.
// Play: https://go.dev/play/p/dPiMRtJ6cUx
func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
length := len(collection)
@@ -75,6 +80,7 @@ func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int
}
// FindOrElse search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.
// Play: https://go.dev/play/p/Eo7W0lvKTky
func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) T {
for i := range collection {
if predicate(collection[i]) {
@@ -86,6 +92,7 @@ func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool)
}
// FindKey returns the key of the first value matching.
// Play: https://go.dev/play/p/Bg0w1VDPYXx
func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
for k := range object {
if object[k] == value {
@@ -97,6 +104,7 @@ func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
}
// FindKeyBy returns the key of the first element predicate returns truthy for.
// Play: https://go.dev/play/p/9IbiPElcyo8
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool) {
for k := range object {
if predicate(k, object[k]) {
@@ -221,6 +229,7 @@ func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iterate
// Min search the minimum value of a collection.
// Returns zero value when the collection is empty.
// Play: https://go.dev/play/p/r6e-Z8JozS8
func Min[T constraints.Ordered](collection []T) T {
var min T
@@ -365,6 +374,7 @@ func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T {
// Max searches the maximum value of a collection.
// Returns zero value when the collection is empty.
// Play: https://go.dev/play/p/r6e-Z8JozS8
func Max[T constraints.Ordered](collection []T) T {
var max T
@@ -508,6 +518,7 @@ func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T {
}
// First returns the first element of a collection and check for availability of the first element.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func First[T any](collection []T) (T, bool) {
length := len(collection)
@@ -520,12 +531,14 @@ func First[T any](collection []T) (T, bool) {
}
// FirstOrEmpty returns the first element of a collection or zero value if empty.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func FirstOrEmpty[T any](collection []T) T {
i, _ := First(collection)
return i
}
// FirstOr returns the first element of a collection or the fallback value if empty.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func FirstOr[T any](collection []T, fallback T) T {
i, ok := First(collection)
if !ok {
@@ -536,6 +549,7 @@ func FirstOr[T any](collection []T, fallback T) T {
}
// Last returns the last element of a collection or error if empty.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func Last[T any](collection []T) (T, bool) {
length := len(collection)
@@ -548,12 +562,14 @@ func Last[T any](collection []T) (T, bool) {
}
// LastOrEmpty returns the last element of a collection or zero value if empty.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func LastOrEmpty[T any](collection []T) T {
i, _ := Last(collection)
return i
}
// LastOr returns the last element of a collection or the fallback value if empty.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func LastOr[T any](collection []T, fallback T) T {
i, ok := Last(collection)
if !ok {
@@ -565,6 +581,7 @@ func LastOr[T any](collection []T, fallback T) T {
// Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element
// from the end is returned. An error is returned when nth is out of slice bounds.
// Play: https://go.dev/play/p/sHoh88KWt6B
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
n := int(nth)
l := len(collection)
@@ -582,6 +599,7 @@ func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
// NthOr returns the element at index `nth` of collection.
// If `nth` is negative, it returns the nth element from the end.
// If `nth` is out of slice bounds, it returns the fallback value instead of an error.
// Play: https://go.dev/play/p/sHoh88KWt6B
func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
value, err := Nth(collection, nth)
if err != nil {
@@ -593,6 +611,7 @@ func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
// NthOrEmpty returns the element at index `nth` of collection.
// If `nth` is negative, it returns the nth element from the end.
// If `nth` is out of slice bounds, it returns the zero value (empty value) for that type.
// Play: https://go.dev/play/p/sHoh88KWt6B
func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T {
value, err := Nth(collection, nth)
if err != nil {
@@ -607,12 +626,14 @@ func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T {
type randomIntGenerator func(n int) int
// Sample returns a random item from collection.
// Play: https://go.dev/play/p/vCcSJbh5s6l
func Sample[T any](collection []T) T {
result := SampleBy(collection, rand.IntN)
return result
}
// SampleBy returns a random item from collection, using randomIntGenerator as the random index generator.
// Play: https://go.dev/play/p/HDmKmMgq0XN
func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
size := len(collection)
if size == 0 {
@@ -622,12 +643,14 @@ func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
}
// Samples returns N random unique items from collection.
// Play: https://go.dev/play/p/vCcSJbh5s6l
func Samples[T any, Slice ~[]T](collection Slice, count int) Slice {
results := SamplesBy(collection, count, rand.IntN)
return results
}
// SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator.
// Play: https://go.dev/play/p/HDmKmMgq0XN
func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator randomIntGenerator) Slice {
size := len(collection)

View File

@@ -1,6 +1,7 @@
package lo
// Partial returns new function that, when called, has its first argument set to the provided value.
// Play: https://go.dev/play/p/Sy1gAQiQZ3v
func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R {
return func(t2 T2) R {
return f(arg1, t2)
@@ -8,11 +9,13 @@ func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R {
}
// Partial1 returns new function that, when called, has its first argument set to the provided value.
// Play: https://go.dev/play/p/D-ASTXCLBzw
func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R {
return Partial(f, arg1)
}
// Partial2 returns new function that, when called, has its first argument set to the provided value.
// Play: https://go.dev/play/p/-xiPjy4JChJ
func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R {
return func(t2 T2, t3 T3) R {
return f(arg1, t2, t3)
@@ -20,6 +23,7 @@ func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R {
}
// Partial3 returns new function that, when called, has its first argument set to the provided value.
// Play: https://go.dev/play/p/zWtSutpI26m
func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, T3, T4) R {
return func(t2 T2, t3 T3, t4 T4) R {
return f(arg1, t2, t3, t4)
@@ -27,6 +31,7 @@ func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2,
}
// Partial4 returns new function that, when called, has its first argument set to the provided value.
// Play: https://go.dev/play/p/kBrnnMTcJm0
func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1) func(T2, T3, T4, T5) R {
return func(t2 T2, t3 T3, t4 T4, t5 T5) R {
return f(arg1, t2, t3, t4, t5)
@@ -34,6 +39,7 @@ func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1)
}
// Partial5 returns new function that, when called, has its first argument set to the provided value
// Play: https://go.dev/play/p/7Is7K2y_VC3
func Partial5[T1, T2, T3, T4, T5, T6, R any](f func(T1, T2, T3, T4, T5, T6) R, arg1 T1) func(T2, T3, T4, T5, T6) R {
return func(t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R {
return f(arg1, t2, t3, t4, t5, t6)

View File

@@ -1,6 +1,7 @@
package lo
// Contains returns true if an element is present in a collection.
// Play: https://go.dev/play/p/W1EvyqY6t9j
func Contains[T comparable](collection []T, element T) bool {
for i := range collection {
if collection[i] == element {
@@ -12,6 +13,7 @@ func Contains[T comparable](collection []T, element T) bool {
}
// ContainsBy returns true if predicate function return true.
// Play: https://go.dev/play/p/W1EvyqY6t9j
func ContainsBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
@@ -23,6 +25,7 @@ func ContainsBy[T any](collection []T, predicate func(item T) bool) bool {
}
// Every returns true if all elements of a subset are contained into a collection or if the subset is empty.
// Play: https://go.dev/play/p/W1EvyqY6t9j
func Every[T comparable](collection []T, subset []T) bool {
for i := range subset {
if !Contains(collection, subset[i]) {
@@ -34,6 +37,7 @@ func Every[T comparable](collection []T, subset []T) bool {
}
// EveryBy returns true if the predicate returns true for all elements in the collection or if the collection is empty.
// Play: https://go.dev/play/p/dn1-vhHsq9x
func EveryBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if !predicate(collection[i]) {
@@ -46,6 +50,7 @@ func EveryBy[T any](collection []T, predicate func(item T) bool) bool {
// Some returns true if at least 1 element of a subset is contained into a collection.
// If the subset is empty Some returns false.
// Play: https://go.dev/play/p/Lj4ceFkeT9V
func Some[T comparable](collection []T, subset []T) bool {
for i := range subset {
if Contains(collection, subset[i]) {
@@ -58,6 +63,7 @@ func Some[T comparable](collection []T, subset []T) bool {
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
// If the collection is empty SomeBy returns false.
// Play: https://go.dev/play/p/DXF-TORBudx
func SomeBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
@@ -69,6 +75,7 @@ func SomeBy[T any](collection []T, predicate func(item T) bool) bool {
}
// None returns true if no element of a subset are contained into a collection or if the subset is empty.
// Play: https://go.dev/play/p/fye7JsmxzPV
func None[T comparable](collection []T, subset []T) bool {
for i := range subset {
if Contains(collection, subset[i]) {
@@ -80,6 +87,7 @@ func None[T comparable](collection []T, subset []T) bool {
}
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
// Play: https://go.dev/play/p/O64WZ32H58S
func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
@@ -91,6 +99,7 @@ func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
}
// Intersect returns the intersection between two collections.
// Play: https://go.dev/play/p/uuElL9X9e58
func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice {
result := Slice{}
seen := map[T]struct{}{}
@@ -111,6 +120,7 @@ func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice {
// Difference returns the difference between two collections.
// The first value is the collection of element absent of list2.
// The second value is the collection of element absent of list1.
// Play: https://go.dev/play/p/pKE-JgzqRpz
func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slice) {
left := Slice{}
right := Slice{}
@@ -143,6 +153,7 @@ func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slic
// Union returns all distinct elements from given collections.
// result returns will not change the order of elements relatively.
// Play: https://go.dev/play/p/DI9RVEB_qMK
func Union[T comparable, Slice ~[]T](lists ...Slice) Slice {
var capLen int
@@ -166,6 +177,7 @@ func Union[T comparable, Slice ~[]T](lists ...Slice) Slice {
}
// Without returns slice excluding all given values.
// Play: https://go.dev/play/p/5j30Ux8TaD0
func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice {
excludeMap := make(map[T]struct{}, len(exclude))
for i := range exclude {
@@ -183,6 +195,7 @@ func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice {
// WithoutBy filters a slice by excluding elements whose extracted keys match any in the exclude list.
// It returns a new slice containing only the elements whose keys are not in the exclude list.
// Play: https://go.dev/play/p/VgWJOF01NbJ
func WithoutBy[T any, K comparable](collection []T, iteratee func(item T) K, exclude ...K) []T {
excludeMap := make(map[K]struct{}, len(exclude))
for _, e := range exclude {
@@ -206,6 +219,7 @@ func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) Slice {
}
// WithoutNth returns slice excluding nth value.
// Play: https://go.dev/play/p/5g3F9R2H1xL
func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) Slice {
length := len(collection)
@@ -229,6 +243,7 @@ func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) Slice {
// ElementsMatch returns true if lists contain the same set of elements (including empty set).
// If there are duplicate elements, the number of appearances of each of them in both lists should match.
// The order of elements is not checked.
// Play: https://go.dev/play/p/XWSEM4Ic_t0
func ElementsMatch[T comparable, Slice ~[]T](list1 Slice, list2 Slice) bool {
return ElementsMatchBy(list1, list2, func(item T) T { return item })
}
@@ -236,6 +251,7 @@ func ElementsMatch[T comparable, Slice ~[]T](list1 Slice, list2 Slice) bool {
// ElementsMatchBy returns true if lists contain the same set of elements' keys (including empty set).
// If there are duplicate keys, the number of appearances of each of them in both lists should match.
// The order of elements is not checked.
// Play: https://go.dev/play/p/XWSEM4Ic_t0
func ElementsMatchBy[T any, K comparable](list1 []T, list2 []T, iteratee func(item T) K) bool {
if len(list1) != len(list2) {
return false

3
map.go
View File

@@ -175,7 +175,7 @@ func OmitByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V)
}
// Entries transforms a map into array of key/value pairs.
// Play:
// Play: https://go.dev/play/p/_t4Xe34-Nl5
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
entries := make([]Entry[K, V], 0, len(in))
@@ -331,6 +331,7 @@ func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, val
// The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice.
// If the boolean is false, the value is not added to the result slice.
// The order of the keys in the input map is not specified and the order of the keys in the output slice is not guaranteed.
// Play: https://go.dev/play/p/jgsD_Kil9pV
func FilterMapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) (R, bool)) []R {
result := make([]R, 0, len(in))

View File

@@ -122,6 +122,7 @@ func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Co
}
// Mean calculates the mean of a collection of numbers.
// Play: https://go.dev/play/p/tPURSuteUsP
func Mean[T constraints.Float | constraints.Integer](collection []T) T {
length := T(len(collection))
if length == 0 {
@@ -132,6 +133,7 @@ func Mean[T constraints.Float | constraints.Integer](collection []T) T {
}
// MeanBy calculates the mean of a collection of numbers using the given return value from the iteration function.
// Play: https://go.dev/play/p/j7TsVwBOZ7P
func MeanBy[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) R) R {
length := R(len(collection))
if length == 0 {

View File

@@ -7,6 +7,7 @@ import "github.com/samber/lo/internal/rand"
// and should return true for elements that should be kept and false for elements that should be removed.
// The function returns the modified slice, which may be shorter than the original if some elements were removed.
// Note that the order of elements in the original slice is preserved in the output.
// Play: https://go.dev/play/p/0jY3Z0B7O_5
func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice {
j := 0
for _, item := range collection {
@@ -36,6 +37,7 @@ func FilterI[T any, Slice ~[]T](collection Slice, predicate func(item T, index i
// Map is a generic function that modifies the input slice in-place to contain the result of applying the provided
// function to each element of the slice. The function returns the modified slice, which has the same length as the original.
// Play: https://go.dev/play/p/0jY3Z0B7O_5
func Map[T any, Slice ~[]T](collection Slice, fn func(item T) T) {
for i := range collection {
collection[i] = fn(collection[i])

View File

@@ -4,6 +4,7 @@ import "sync"
// Map manipulates a slice and transforms it to a slice of another type.
// `iteratee` is call in parallel. Result keep the same order.
// Play: https://go.dev/play/p/sCJaB3quRMC
func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
result := make([]R, len(collection))
@@ -27,6 +28,7 @@ func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
// ForEach iterates over elements of collection and invokes iteratee for each element.
// `iteratee` is call in parallel.
// Play: https://go.dev/play/p/sCJaB3quRMC
func ForEach[T any](collection []T, iteratee func(item T, index int)) {
var wg sync.WaitGroup
wg.Add(len(collection))

View File

@@ -194,6 +194,7 @@ func AttemptWithDelay(maxIteration int, delay time.Duration, f func(index int, d
// immediately if the second return value is false. When the first
// argument is less than `1`, the function runs until a successful response is
// returned.
// Play: https://go.dev/play/p/1VS7HxlYMOG
func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) {
var err error
var shouldContinueInvoke bool
@@ -218,6 +219,7 @@ func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) {
// It will terminate the invoke immediately if the second return value is false.
// When the first argument is less than `1`, the function runs until a successful
// response is returned.
// Play: https://go.dev/play/p/mhufUjJfLEF
func AttemptWhileWithDelay(maxIteration int, delay time.Duration, f func(int, time.Duration) (error, bool)) (int, time.Duration, error) {
var err error
var shouldContinueInvoke bool
@@ -247,6 +249,7 @@ type transactionStep[T any] struct {
}
// NewTransaction instantiate a new transaction.
// Play: https://go.dev/play/p/Qxrd7MGQGh1
func NewTransaction[T any]() *Transaction[T] {
return &Transaction[T]{
steps: []transactionStep[T]{},
@@ -259,6 +262,7 @@ type Transaction[T any] struct {
}
// Then adds a step to the chain of callbacks. It returns the same Transaction.
// Play: https://go.dev/play/p/Qxrd7MGQGh1 https://go.dev/play/p/xrHb2_kMvTY
func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Transaction[T] {
t.steps = append(t.steps, transactionStep[T]{
exec: exec,
@@ -269,6 +273,7 @@ func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Tr
}
// Process runs the Transaction steps and rollbacks in case of errors.
// Play: https://go.dev/play/p/Qxrd7MGQGh1 https://go.dev/play/p/xrHb2_kMvTY
func (t *Transaction[T]) Process(state T) (T, error) {
var i int
var err error
@@ -341,11 +346,13 @@ func (th *throttleBy[T]) reset() {
// NewThrottle creates a throttled instance that invokes given functions only once in every interval.
// This returns 2 functions, First one is throttled function and Second one is a function to reset interval
// Play: https://go.dev/play/p/qQn3fm8Z7jS
func NewThrottle(interval time.Duration, f ...func()) (throttle func(), reset func()) {
return NewThrottleWithCount(interval, 1, f...)
}
// NewThrottleWithCount is NewThrottle with count limit, throttled function will be invoked count times in every interval.
// Play: https://go.dev/play/p/w5nc0MgWtjC
func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throttle func(), reset func()) {
callbacks := Map(f, func(item func(), _ int) func(struct{}) {
return func(struct{}) {
@@ -361,11 +368,13 @@ func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throt
// NewThrottleBy creates a throttled instance that invokes given functions only once in every interval.
// This returns 2 functions, First one is throttled function and Second one is a function to reset interval
// Play: https://go.dev/play/p/0Wv6oX7dHdC
func NewThrottleBy[T comparable](interval time.Duration, f ...func(key T)) (throttle func(key T), reset func()) {
return NewThrottleByWithCount[T](interval, 1, f...)
}
// NewThrottleByWithCount is NewThrottleBy with count limit, throttled function will be invoked count times in every interval.
// Play: https://go.dev/play/p/vQk3ECH7_EW
func NewThrottleByWithCount[T comparable](interval time.Duration, count int, f ...func(key T)) (throttle func(key T), reset func()) {
if count <= 0 {
count = 1

View File

@@ -34,6 +34,7 @@ func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
}
// UniqMap manipulates a slice and transforms it to a slice of another type with unique values.
// Play: https://go.dev/play/p/fygzLBhvUdB
func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index int) R) []R {
result := make([]R, 0, len(collection))
seen := make(map[R]struct{}, len(collection))
@@ -53,7 +54,7 @@ func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index in
// - the result of the mapping operation and
// - whether the result element should be included or not.
//
// Play: https://go.dev/play/p/-AuYXfy7opz
// Play: https://go.dev/play/p/CgHYNUpOd1I
func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R {
result := make([]R, 0, len(collection))
@@ -69,7 +70,7 @@ func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R
// FlatMap manipulates a slice and transforms and flattens it to a slice of another type.
// The transform function can either return a slice or a `nil`, and in the `nil` case
// no value is added to the final slice.
// Play: https://go.dev/play/p/YSoYmQTA8-U
// Play: https://go.dev/play/p/pFCF5WVB225
func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R) []R {
result := make([]R, 0, len(collection))
@@ -82,7 +83,7 @@ func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R)
// Reduce reduces collection to a value which is the accumulated result of running each element in collection
// through accumulator, where each successive invocation is supplied the return value of the previous.
// Play: https://go.dev/play/p/R4UHXZNaaUG
// Play: https://go.dev/play/p/CgHYNUpOd1I
func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R {
for i := range collection {
initial = accumulator(initial, collection[i], i)
@@ -189,6 +190,7 @@ func GroupBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(it
}
// GroupByMap returns an object composed of keys generated from the results of running each element of collection through iteratee.
// Play: https://go.dev/play/p/iMeruQ3_W80
func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T) (K, V)) map[K][]V {
result := map[K][]V{}
@@ -203,7 +205,7 @@ func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T
// 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
// Play: https://go.dev/play/p/kEMkFbdu85g
func Chunk[T any, Slice ~[]T](collection Slice, size int) []Slice {
if size <= 0 {
panic("Second parameter must be greater than 0")
@@ -368,7 +370,7 @@ func RepeatBy[T any](count int, predicate func(index int) T) []T {
}
// 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
// Play: https://go.dev/play/p/ccUiUL_Lnel
func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V {
result := make(map[K]V, len(collection))
@@ -408,6 +410,7 @@ func SliceToMap[T any, K comparable, V any](collection []T, transform func(item
// 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.
// The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map.
// Play: https://go.dev/play/p/2z0rDz2ZSGU
func FilterSliceToMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V, bool)) map[K]V {
result := make(map[K]V, len(collection))
@@ -422,6 +425,7 @@ func FilterSliceToMap[T any, K comparable, V any](collection []T, transform func
}
// Keyify returns a map with each unique element of the slice as a key.
// Play: https://go.dev/play/p/RYhhM_csqIG
func Keyify[T comparable, Slice ~[]T](collection Slice) map[T]struct{} {
result := make(map[T]struct{}, len(collection))
@@ -516,7 +520,7 @@ func DropByIndex[T any](collection []T, indexes ...int) []T {
}
// 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
// Play: https://go.dev/play/p/pFCF5WVB225
func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice {
result := Slice{}
@@ -533,6 +537,8 @@ func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index in
// 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/W9Ug9r0QFkL
func RejectMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R {
result := []R{}
@@ -547,6 +553,7 @@ func RejectMap[T any, R any](collection []T, callback func(item T, index int) (R
// FilterReject mixes Filter and Reject, this method returns two slices, one for the elements of collection that
// predicate returns truthy for and one for the elements that predicate does not return truthy for.
// Play: https://go.dev/play/p/lHSEGSznJjB
func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept Slice, rejected Slice) {
kept = make(Slice, 0, len(collection))
rejected = make(Slice, 0, len(collection))

View File

@@ -151,6 +151,7 @@ func RuneLength(str string) int {
}
// PascalCase converts string to pascal case.
// Play: https://go.dev/play/p/Dy_V_6DUYhe
func PascalCase(str string) string {
items := Words(str)
for i := range items {
@@ -160,6 +161,7 @@ func PascalCase(str string) string {
}
// CamelCase converts string to camel case.
// Play: https://go.dev/play/p/Go6aKwUiq59
func CamelCase(str string) string {
items := Words(str)
for i, item := range items {
@@ -173,6 +175,7 @@ func CamelCase(str string) string {
}
// KebabCase converts string to kebab case.
// Play: https://go.dev/play/p/96gT_WZnTVP
func KebabCase(str string) string {
items := Words(str)
for i := range items {
@@ -182,6 +185,7 @@ func KebabCase(str string) string {
}
// SnakeCase converts string to snake case.
// Play: https://go.dev/play/p/ziB0V89IeVH
func SnakeCase(str string) string {
items := Words(str)
for i := range items {
@@ -191,6 +195,7 @@ func SnakeCase(str string) string {
}
// Words splits string into an array of its words.
// Play: https://go.dev/play/p/-f3VIQqiaVw
func Words(str string) []string {
str = splitWordReg.ReplaceAllString(str, `$1$3$5$7 $2$4$6$8$9`)
// example: Int8Value => Int 8Value => Int 8 Value
@@ -207,6 +212,7 @@ func Words(str string) []string {
}
// Capitalize converts the first character of string to upper case and the remaining to lower case.
// Play: https://go.dev/play/p/uLTZZQXqnsa
func Capitalize(str string) string {
return cases.Title(language.English).String(str)
}
@@ -214,6 +220,7 @@ func Capitalize(str string) string {
// Ellipsis trims and truncates a string to a specified length **in bytes** and appends an ellipsis
// if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8),
// truncating by byte length may split a character in the middle, potentially resulting in garbled output.
// Play: https://go.dev/play/p/qE93rgqe1TW
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)

12
time.go
View File

@@ -3,11 +3,13 @@ package lo
import "time"
// Duration returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
func Duration(cb func()) time.Duration {
return Duration0(cb)
}
// Duration0 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
func Duration0(cb func()) time.Duration {
start := time.Now()
cb()
@@ -15,6 +17,7 @@ func Duration0(cb func()) time.Duration {
}
// Duration1 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
func Duration1[A any](cb func() A) (A, time.Duration) {
start := time.Now()
a := cb()
@@ -22,6 +25,7 @@ func Duration1[A any](cb func() A) (A, time.Duration) {
}
// Duration2 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) {
start := time.Now()
a, b := cb()
@@ -29,6 +33,7 @@ func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) {
}
// Duration3 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) {
start := time.Now()
a, b, c := cb()
@@ -36,6 +41,7 @@ func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) {
}
// Duration4 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duration) {
start := time.Now()
a, b, c, d := cb()
@@ -43,6 +49,7 @@ func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duratio
}
// Duration5 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, time.Duration) {
start := time.Now()
a, b, c, d, e := cb()
@@ -50,6 +57,7 @@ func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, tim
}
// Duration6 returns the time taken to execute a function.
// Play: https://go.dev/play/p/mR4bTQKO-Tf
func Duration6[A, B, C, D, E, F any](cb func() (A, B, C, D, E, F)) (A, B, C, D, E, F, time.Duration) {
start := time.Now()
a, b, c, d, e, f := cb()
@@ -57,6 +65,7 @@ func Duration6[A, B, C, D, E, F any](cb func() (A, B, C, D, E, F)) (A, B, C, D,
}
// Duration7 returns the time taken to execute a function.
// Play: https://go.dev/play/p/jgIAcBWWInS
func Duration7[A, B, C, D, E, F, G any](cb func() (A, B, C, D, E, F, G)) (A, B, C, D, E, F, G, time.Duration) {
start := time.Now()
a, b, c, d, e, f, g := cb()
@@ -64,6 +73,7 @@ func Duration7[A, B, C, D, E, F, G any](cb func() (A, B, C, D, E, F, G)) (A, B,
}
// Duration8 returns the time taken to execute a function.
// Play: https://go.dev/play/p/T8kxpG1c5Na
func Duration8[A, B, C, D, E, F, G, H any](cb func() (A, B, C, D, E, F, G, H)) (A, B, C, D, E, F, G, H, time.Duration) {
start := time.Now()
a, b, c, d, e, f, g, h := cb()
@@ -71,6 +81,7 @@ func Duration8[A, B, C, D, E, F, G, H any](cb func() (A, B, C, D, E, F, G, H)) (
}
// Duration9 returns the time taken to execute a function.
// Play: https://go.dev/play/p/bg9ix2VrZ0j
func Duration9[A, B, C, D, E, F, G, H, I any](cb func() (A, B, C, D, E, F, G, H, I)) (A, B, C, D, E, F, G, H, I, time.Duration) {
start := time.Now()
a, b, c, d, e, f, g, h, i := cb()
@@ -78,6 +89,7 @@ func Duration9[A, B, C, D, E, F, G, H, I any](cb func() (A, B, C, D, E, F, G, H,
}
// Duration10 returns the time taken to execute a function.
// Play: https://go.dev/play/p/Y3n7oJXqJbk
func Duration10[A, B, C, D, E, F, G, H, I, J any](cb func() (A, B, C, D, E, F, G, H, I, J)) (A, B, C, D, E, F, G, H, I, J, time.Duration) {
start := time.Now()
a, b, c, d, e, f, g, h, i, j := cb()

View File

@@ -331,6 +331,7 @@ func Zip9[A, B, C, D, E, F, G, H, I any](a []A, b []B, c []C, d []D, e []E, f []
// ZipBy2 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/wlHur6yO8rR
func ZipBy2[A any, B any, Out any](a []A, b []B, iteratee func(a A, b B) Out) []Out {
size := Max([]int{len(a), len(b)})
@@ -349,6 +350,7 @@ func ZipBy2[A any, B any, Out any](a []A, b []B, iteratee func(a A, b B) Out) []
// ZipBy3 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/j9maveOnSQX
func ZipBy3[A any, B any, C any, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C) Out) []Out {
size := Max([]int{len(a), len(b), len(c)})
@@ -368,6 +370,7 @@ func ZipBy3[A any, B any, C any, Out any](a []A, b []B, c []C, iteratee func(a A
// ZipBy4 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/Y1eF2Ke0Ayz
func ZipBy4[A any, B any, C any, D any, Out any](a []A, b []B, c []C, d []D, iteratee func(a A, b B, c C, d D) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d)})
@@ -388,6 +391,7 @@ func ZipBy4[A any, B any, C any, D any, Out any](a []A, b []B, c []C, d []D, ite
// ZipBy5 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/SLynyalh5Oa
func ZipBy5[A any, B any, C any, D any, E any, Out any](a []A, b []B, c []C, d []D, e []E, iteratee func(a A, b B, c C, d D, e E) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d), len(e)})
@@ -409,6 +413,7 @@ func ZipBy5[A any, B any, C any, D any, E any, Out any](a []A, b []B, c []C, d [
// ZipBy6 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/IK6KVgw9e-S
func ZipBy6[A any, B any, C any, D any, E any, F any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, iteratee func(a A, b B, c C, d D, e E, f F) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)})
@@ -431,6 +436,7 @@ func ZipBy6[A any, B any, C any, D any, E any, F any, Out any](a []A, b []B, c [
// ZipBy7 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/4uW6a2vXh8w
func ZipBy7[A any, B any, C any, D any, E any, F any, G any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, iteratee func(a A, b B, c C, d D, e E, f F, g G) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)})
@@ -454,6 +460,7 @@ func ZipBy7[A any, B any, C any, D any, E any, F any, G any, Out any](a []A, b [
// ZipBy8 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/tk8xW7XzY4v
func ZipBy8[A any, B any, C any, D any, E any, F any, G any, H any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)})
@@ -478,6 +485,7 @@ func ZipBy8[A any, B any, C any, D any, E any, F any, G any, H any, Out any](a [
// ZipBy9 creates a slice of transformed elements, the first of which contains the first elements
// of the given arrays, the second of which contains the second elements of the given arrays, and so on.
// When collections have different size, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/VGqjDmQ9YqX
func ZipBy9[A any, B any, C any, D any, E any, F any, G any, H any, I any, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out {
size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)})
@@ -686,6 +694,7 @@ func Unzip9[A, B, C, D, E, F, G, H, I any](tuples []Tuple9[A, B, C, D, E, F, G,
// UnzipBy2 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/tN8yqaRZz0r
func UnzipBy2[In any, A any, B any](items []In, iteratee func(In) (a A, b B)) ([]A, []B) {
size := len(items)
r1 := make([]A, 0, size)
@@ -702,6 +711,7 @@ func UnzipBy2[In any, A any, B any](items []In, iteratee func(In) (a A, b B)) ([
// UnzipBy3 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/36ITO2DlQq1
func UnzipBy3[In any, A any, B any, C any](items []In, iteratee func(In) (a A, b B, c C)) ([]A, []B, []C) {
size := len(items)
r1 := make([]A, 0, size)
@@ -720,6 +730,7 @@ func UnzipBy3[In any, A any, B any, C any](items []In, iteratee func(In) (a A, b
// UnzipBy4 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/zJ6qY1dD1rL
func UnzipBy4[In any, A any, B any, C any, D any](items []In, iteratee func(In) (a A, b B, c C, d D)) ([]A, []B, []C, []D) {
size := len(items)
r1 := make([]A, 0, size)
@@ -740,6 +751,7 @@ func UnzipBy4[In any, A any, B any, C any, D any](items []In, iteratee func(In)
// UnzipBy5 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/3f7jKkV9xZt
func UnzipBy5[In any, A any, B any, C any, D any, E any](items []In, iteratee func(In) (a A, b B, c C, d D, e E)) ([]A, []B, []C, []D, []E) {
size := len(items)
r1 := make([]A, 0, size)
@@ -762,6 +774,7 @@ func UnzipBy5[In any, A any, B any, C any, D any, E any](items []In, iteratee fu
// UnzipBy6 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/8Y1b7tKu2pL
func UnzipBy6[In any, A any, B any, C any, D any, E any, F any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F)) ([]A, []B, []C, []D, []E, []F) {
size := len(items)
r1 := make([]A, 0, size)
@@ -786,6 +799,7 @@ func UnzipBy6[In any, A any, B any, C any, D any, E any, F any](items []In, iter
// UnzipBy7 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/7j1kLmVn3pM
func UnzipBy7[In any, A any, B any, C any, D any, E any, F any, G any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G)) ([]A, []B, []C, []D, []E, []F, []G) {
size := len(items)
r1 := make([]A, 0, size)
@@ -812,6 +826,7 @@ func UnzipBy7[In any, A any, B any, C any, D any, E any, F any, G any](items []I
// UnzipBy8 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/1n2k3L4m5N6
func UnzipBy8[In any, A any, B any, C any, D any, E any, F any, G any, H any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H)) ([]A, []B, []C, []D, []E, []F, []G, []H) {
size := len(items)
r1 := make([]A, 0, size)
@@ -840,6 +855,7 @@ func UnzipBy8[In any, A any, B any, C any, D any, E any, F any, G any, H any](it
// UnzipBy9 iterates over a collection and creates an array regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/7o8p9q0r1s2
func UnzipBy9[In any, A any, B any, C any, D any, E any, F any, G any, H any, I any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H, i I)) ([]A, []B, []C, []D, []E, []F, []G, []H, []I) {
size := len(items)
r1 := make([]A, 0, size)
@@ -871,6 +887,7 @@ func UnzipBy9[In any, A any, B any, C any, D any, E any, F any, G any, H any, I
// CrossJoin2 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/3VFppyL9FDU
func CrossJoin2[A, B any](listA []A, listB []B) []Tuple2[A, B] {
return CrossJoinBy2(listA, listB, T2[A, B])
}
@@ -878,6 +895,7 @@ func CrossJoin2[A, B any](listA []A, listB []B) []Tuple2[A, B] {
// CrossJoin3 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/2WGeHyJj4fK
func CrossJoin3[A, B, C any](listA []A, listB []B, listC []C) []Tuple3[A, B, C] {
return CrossJoinBy3(listA, listB, listC, T3[A, B, C])
}
@@ -885,6 +903,7 @@ func CrossJoin3[A, B, C any](listA []A, listB []B, listC []C) []Tuple3[A, B, C]
// CrossJoin4 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/6XhKjLmMnNp
func CrossJoin4[A, B, C, D any](listA []A, listB []B, listC []C, listD []D) []Tuple4[A, B, C, D] {
return CrossJoinBy4(listA, listB, listC, listD, T4[A, B, C, D])
}
@@ -892,6 +911,7 @@ func CrossJoin4[A, B, C, D any](listA []A, listB []B, listC []C, listD []D) []Tu
// CrossJoin5 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/7oPqRsTuVwX
func CrossJoin5[A, B, C, D, E any](listA []A, listB []B, listC []C, listD []D, listE []E) []Tuple5[A, B, C, D, E] {
return CrossJoinBy5(listA, listB, listC, listD, listE, T5[A, B, C, D, E])
}
@@ -899,6 +919,7 @@ func CrossJoin5[A, B, C, D, E any](listA []A, listB []B, listC []C, listD []D, l
// CrossJoin6 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/8yZ1aB2cD3e
func CrossJoin6[A, B, C, D, E, F any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F) []Tuple6[A, B, C, D, E, F] {
return CrossJoinBy6(listA, listB, listC, listD, listE, listF, T6[A, B, C, D, E, F])
}
@@ -906,6 +927,7 @@ func CrossJoin6[A, B, C, D, E, F any](listA []A, listB []B, listC []C, listD []D
// CrossJoin7 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/9f4g5h6i7j8
func CrossJoin7[A, B, C, D, E, F, G any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G) []Tuple7[A, B, C, D, E, F, G] {
return CrossJoinBy7(listA, listB, listC, listD, listE, listF, listG, T7[A, B, C, D, E, F, G])
}
@@ -913,6 +935,7 @@ func CrossJoin7[A, B, C, D, E, F, G any](listA []A, listB []B, listC []C, listD
// CrossJoin8 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/0k1l2m3n4o5
func CrossJoin8[A, B, C, D, E, F, G, H any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H) []Tuple8[A, B, C, D, E, F, G, H] {
return CrossJoinBy8(listA, listB, listC, listD, listE, listF, listG, listH, T8[A, B, C, D, E, F, G, H])
}
@@ -920,6 +943,7 @@ func CrossJoin8[A, B, C, D, E, F, G, H any](listA []A, listB []B, listC []C, lis
// CrossJoin9 combines every items from one list with every items from others.
// It is the cartesian product of lists received as arguments.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/6p7q8r9s0t1
func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I) []Tuple9[A, B, C, D, E, F, G, H, I] {
return CrossJoinBy9(listA, listB, listC, listD, listE, listF, listG, listH, listI, T9[A, B, C, D, E, F, G, H, I])
}
@@ -928,6 +952,7 @@ func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA []A, listB []B, listC []C,
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/8Y7btpvuA-C
func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Out) []Out {
size := len(listA) * len(listB)
if size == 0 {
@@ -949,6 +974,7 @@ func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Ou
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/3z4y5x6w7v8
func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project func(a A, b B, c C) Out) []Out {
size := len(listA) * len(listB) * len(listC)
if size == 0 {
@@ -972,6 +998,7 @@ func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project fun
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/8b9c0d1e2f3
func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, project func(a A, b B, c C, d D) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD)
if size == 0 {
@@ -997,6 +1024,7 @@ func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/4g5h6i7j8k9
func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, project func(a A, b B, c C, d D, e E) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE)
if size == 0 {
@@ -1024,6 +1052,7 @@ func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/1l2m3n4o5p6
func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, project func(a A, b B, c C, d D, e E, f F) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF)
if size == 0 {
@@ -1053,6 +1082,7 @@ func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, li
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/7q8r9s0t1u2
func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, project func(a A, b B, c C, d D, e E, f F, g G) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG)
if size == 0 {
@@ -1084,6 +1114,7 @@ func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C,
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/3v4w5x6y7z8
func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, project func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH)
if size == 0 {
@@ -1117,6 +1148,7 @@ func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC [
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// It returns an empty list if a list is empty.
// Play: https://go.dev/play/p/9a0b1c2d3e4
func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, project func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) * len(listI)
if size == 0 {

View File

@@ -3,28 +3,33 @@ package lo
import "reflect"
// IsNil checks if a value is nil or if it's a reference type with a nil underlying value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func IsNil(x any) bool {
defer func() { recover() }() // nolint:errcheck
return x == nil || reflect.ValueOf(x).IsNil()
}
// IsNotNil checks if a value is not nil or if it's not a reference type with a nil underlying value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func IsNotNil(x any) bool {
return !IsNil(x)
}
// ToPtr returns a pointer copy of value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func ToPtr[T any](x T) *T {
return &x
}
// Nil returns a nil pointer of type.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func Nil[T any]() *T {
return nil
}
// EmptyableToPtr returns a pointer copy of value if it's nonzero.
// Otherwise, returns nil pointer.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func EmptyableToPtr[T any](x T) *T {
// 🤮
isZero := reflect.ValueOf(&x).Elem().IsZero()
@@ -36,6 +41,7 @@ func EmptyableToPtr[T any](x T) *T {
}
// FromPtr returns the pointer value or empty.
// Play: https://go.dev/play/p/mhD9CwO3X0m
func FromPtr[T any](x *T) T {
if x == nil {
return Empty[T]()
@@ -45,6 +51,7 @@ func FromPtr[T any](x *T) T {
}
// FromPtrOr returns the pointer value or the fallback value.
// Play: https://go.dev/play/p/mhD9CwO3X0m
func FromPtrOr[T any](x *T, fallback T) T {
if x == nil {
return fallback
@@ -54,6 +61,7 @@ func FromPtrOr[T any](x *T, fallback T) T {
}
// ToSlicePtr returns a slice of pointer copy of value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func ToSlicePtr[T any](collection []T) []*T {
result := make([]*T, len(collection))
@@ -65,6 +73,7 @@ func ToSlicePtr[T any](collection []T) []*T {
// FromSlicePtr returns a slice with the pointer values.
// Returns a zero value in case of a nil pointer element.
// Play: https://go.dev/play/p/lbunFvzlUDX
func FromSlicePtr[T any](collection []*T) []T {
return Map(collection, func(x *T, _ int) T {
if x == nil {
@@ -86,6 +95,7 @@ func FromSlicePtrOr[T any](collection []*T, fallback T) []T {
}
// ToAnySlice returns a slice with all elements mapped to `any` type
// Play: https://go.dev/play/p/P2sD0PMXw4F
func ToAnySlice[T any](collection []T) []any {
result := make([]any, len(collection))
for i := range collection {
@@ -96,6 +106,7 @@ func ToAnySlice[T any](collection []T) []any {
// FromAnySlice returns an `any` slice with all elements mapped to a type.
// Returns false in case of type conversion failure.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func FromAnySlice[T any](in []any) (out []T, ok bool) {
defer func() {
if r := recover(); r != nil {
@@ -112,24 +123,28 @@ func FromAnySlice[T any](in []any) (out []T, ok bool) {
}
// Empty returns the zero value (https://go.dev/ref/spec#The_zero_value).
// Play: https://go.dev/play/p/P2sD0PMXw4F
func Empty[T any]() T {
var zero T
return zero
}
// IsEmpty returns true if argument is a zero value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func IsEmpty[T comparable](v T) bool {
var zero T
return zero == v
}
// IsNotEmpty returns true if argument is not a zero value.
// Play: https://go.dev/play/p/P2sD0PMXw4F
func IsNotEmpty[T comparable](v T) bool {
var zero T
return zero != v
}
// Coalesce returns the first non-empty arguments. Arguments must be comparable.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func Coalesce[T comparable](values ...T) (result T, ok bool) {
for i := range values {
if values[i] != result {
@@ -143,12 +158,14 @@ func Coalesce[T comparable](values ...T) (result T, ok bool) {
}
// CoalesceOrEmpty returns the first non-empty arguments. Arguments must be comparable.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceOrEmpty[T comparable](v ...T) T {
result, _ := Coalesce(v...)
return result
}
// CoalesceSlice returns the first non-zero slice.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceSlice[T any](v ...[]T) ([]T, bool) {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {
@@ -159,6 +176,7 @@ func CoalesceSlice[T any](v ...[]T) ([]T, bool) {
}
// CoalesceSliceOrEmpty returns the first non-zero slice.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceSliceOrEmpty[T any](v ...[]T) []T {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {
@@ -169,6 +187,7 @@ func CoalesceSliceOrEmpty[T any](v ...[]T) []T {
}
// CoalesceMap returns the first non-zero map.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {
@@ -179,6 +198,7 @@ func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) {
}
// CoalesceMapOrEmpty returns the first non-zero map.
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {

View File

@@ -13,6 +13,7 @@ type Tuple2[A, B any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/yrtn7QJTmL_E
func (t Tuple2[A, B]) Unpack() (A, B) {
return t.A, t.B
}
@@ -25,6 +26,7 @@ type Tuple3[A, B, C any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/yrtn7QJTmL_E
func (t Tuple3[A, B, C]) Unpack() (A, B, C) {
return t.A, t.B, t.C
}
@@ -38,6 +40,7 @@ type Tuple4[A, B, C, D any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/yrtn7QJTmL_E
func (t Tuple4[A, B, C, D]) Unpack() (A, B, C, D) {
return t.A, t.B, t.C, t.D
}
@@ -52,6 +55,7 @@ type Tuple5[A, B, C, D, E any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/7J4KrtgtK3M
func (t Tuple5[A, B, C, D, E]) Unpack() (A, B, C, D, E) {
return t.A, t.B, t.C, t.D, t.E
}
@@ -67,6 +71,7 @@ type Tuple6[A, B, C, D, E, F any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/7J4KrtgtK3M
func (t Tuple6[A, B, C, D, E, F]) Unpack() (A, B, C, D, E, F) {
return t.A, t.B, t.C, t.D, t.E, t.F
}
@@ -83,6 +88,7 @@ type Tuple7[A, B, C, D, E, F, G any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/Ow9Zgf_zeiA
func (t Tuple7[A, B, C, D, E, F, G]) Unpack() (A, B, C, D, E, F, G) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G
}
@@ -100,6 +106,7 @@ type Tuple8[A, B, C, D, E, F, G, H any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/Ow9Zgf_zeiA
func (t Tuple8[A, B, C, D, E, F, G, H]) Unpack() (A, B, C, D, E, F, G, H) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H
}
@@ -118,6 +125,7 @@ type Tuple9[A, B, C, D, E, F, G, H, I any] struct {
}
// Unpack returns values contained in tuple.
// Play: https://go.dev/play/p/Ow9Zgf_zeiA
func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unpack() (A, B, C, D, E, F, G, H, I) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H, t.I
}