package collection
// Aggregate aggregates the elements of the slice into a single value using a user-defined aggregator function.
func Aggregate[T, K any](source []T, aggregator func(K, T) K) K {
var result K
for _, v := range source {
result = aggregator(result, v)
}
return result
}
package collection
import "sync"
// ChannelsReadonly transforms input N channels to receive only channels
func ChannelsReadonly[T any](args ...chan T) []<-chan T {
return TransformBy(args, func(v chan T) <-chan T {
return v
})
}
// ChannelsMerge merge input from N channels to 1 receive only channel
func ChannelsMerge[T any](args ...<-chan T) <-chan T {
result := make(chan T)
wg := sync.WaitGroup{}
wg.Add(len(args))
go func() {
wg.Wait()
close(result)
}()
for _, c := range args {
go func(c <-chan T) {
for v := range c {
result <- v
}
wg.Done()
}(c)
}
return result
}
package collection
import (
"golang.org/x/exp/constraints"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// Min returns the smaller of x or y.
func Min[T constraints.Ordered](l T, r T) T {
if l <= r {
return l
}
return r
}
// Max returns the larger of x or y.
func Max[T constraints.Ordered](l T, r T) T {
if l >= r {
return l
}
return r
}
// MinOf returns the smallest value among the provided elements or zero value
func MinOf[T constraints.Ordered](elements ...T) T {
if len(elements) == 0 {
var zero T
return zero
}
min := elements[0]
for _, v := range elements {
if v < min {
min = v
}
}
return min
}
// MaxOf returns the largest value among the provided elements or zero value
func MaxOf[T constraints.Ordered](elements ...T) T {
if len(elements) == 0 {
var zero T
return zero
}
max := elements[0]
for _, v := range elements {
if v > max {
max = v
}
}
return max
}
// Equal is equal to slices.Equal
func Equal[T ~[]E, E comparable](s1, s2 T) bool {
return slices.Equal(s1, s2)
}
// MapEqual is equal to maps.Equal
func MapEqual[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
return maps.Equal(m1, m2)
}
package collection
// Copy returns copy of the slice
func Copy[T any](source []T) []T {
var result = make([]T, len(source))
copy(result, source)
return result
}
package collection
type Filter[T any] func(T) bool
// InFilter returns a filter function that filters elements based on whether they are present or absent in the given slice.
func InFilter[T comparable](source []T, present bool) Filter[T] {
var set = make(map[T]struct{}, len(source))
for _, v := range source {
set[v] = struct{}{}
}
return func(item T) bool {
_, ok := set[item]
return ok == present
}
}
// FilterBy returns a new slice with only the elements that satisfy the given filter function.
func FilterBy[T any](source []T, filter Filter[T]) []T {
var result = make([]T, 0, len(source))
for _, item := range source {
if filter(item) {
result = append(result, item)
}
}
return result
}
// MapFilterBy returns a new map with only the key-value pairs that satisfy the given filter function.
func MapFilterBy[K comparable, T any](source map[K]T, filter func(key K, value T) bool) map[K]T {
var result = make(map[K]T, len(source))
for key, value := range source {
if filter(key, value) {
result[key] = value
}
}
return result
}
// Distinct returns a new slice with all duplicate elements removed.
func Distinct[T comparable](source []T) []T {
var (
set = make(map[T]struct{}, len(source))
result = make([]T, 0, len(source))
)
for _, v := range source {
if _, ok := set[v]; !ok {
set[v] = struct{}{}
result = append(result, v)
}
}
return result
}
// DistinctBy returns a new slice with all duplicate elements removed.
func DistinctBy[T any](source []T, equals func(left T, right T) bool) []T {
var result = make([]T, 0, len(source))
sourceLoop:
for _, v := range source {
for i, u := range result {
if equals(v, u) {
result[i] = v
continue sourceLoop
}
}
result = append(result, v)
}
return result
}
// Difference finds a set difference between a and b
// (values that are in a but not in b or a-b).
func Difference[T comparable](a []T, b []T) []T {
return FilterBy(a, InFilter(b, false))
}
// Intersection finds a set intersection between a and b
// (unique values that are in a and in b).
func Intersection[T comparable](a []T, b []T) []T {
return Distinct(FilterBy(a, InFilter(b, true)))
}
package collection
// GroupBy groups the elements of the slice by a key returned by the given key function.
func GroupBy[T any, K comparable](source []T, keyFunc func(T) K) map[K][]T {
var result = make(map[K][]T)
for _, v := range source {
var key = keyFunc(v)
result[key] = append(result[key], v)
}
return result
}
package collection
// Each calls the given function for each element in the slice.
func Each[T any](source []T, do func(T)) {
for _, v := range source {
do(v)
}
}
// MapEach calls the given function for each key-value pair in the map.
func MapEach[K comparable, T any](source map[K]T, do func(key K, value T)) {
for k, v := range source {
do(k, v)
}
}
package collection
import (
"sync"
)
type Pair[T any, V any] struct {
First T
Second V
}
type KV[K comparable, T any] struct {
Key K
Value T
}
// MapKeys returns a new slice containing all keys in the map.
func MapKeys[K comparable, T any](source map[K]T) []K {
var result = make([]K, 0, len(source))
for key := range source {
result = append(result, key)
}
return result
}
// MapValues returns a slice of type T containing the values of the source map of type T, ordered by key.
func MapValues[K comparable, T any](source map[K]T) []T {
var result = make([]T, 0, len(source))
for _, value := range source {
result = append(result, value)
}
return result
}
type SyncMap[K comparable, V any] struct {
m sync.Map
}
// Store sets the value for a key.
func (m *SyncMap[K, V]) Store(key K, value V) {
m.m.Store(key, value)
}
// Load returns the value stored in the map for a key, or zero value if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
if v, ok := m.m.Load(key); ok {
return v.(V), ok
}
return value, false
}
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
a, loaded := m.m.LoadOrStore(key, value)
return a.(V), loaded
}
// Delete deletes the value for a key.
func (m *SyncMap[K, V]) Delete(key K) {
m.m.Delete(key)
}
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
if v, loaded := m.m.LoadAndDelete(key); loaded {
return v.(V), loaded
}
return value, false
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration. Read sync.Map Range for more details
func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) {
m.m.Range(func(key, value any) bool {
return f(key.(K), value.(V))
})
}
// Swap swaps the value for a key and returns the previous value if any.
// The loaded result reports whether the key was present.
func (m *SyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
if v, loaded := m.m.Swap(key, value); loaded {
return v.(V), loaded
}
return previous, false
}
// CompareAndSwap swaps the old and new values for key
// if the value stored in the map is equal to old.
// The old value must be of a comparable type.
func (m *SyncMap[K, V]) CompareAndSwap(key K, old V, new V) bool {
return m.m.CompareAndSwap(key, old, new)
}
// CompareAndDelete deletes the entry for key if its value is equal to old.
// The old value must be of a comparable type.
func (m *SyncMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
return m.m.CompareAndDelete(key, old)
}
package collection
import (
"sync"
)
type SafeMap[K comparable, V any] struct {
mu sync.RWMutex
m map[K]V
}
func NewSafeMap[K comparable, V any]() *SafeMap[K, V] {
return &SafeMap[K, V]{m: make(map[K]V)}
}
func (s *SafeMap[K, V]) Get(key K) (v V, ok bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok = s.m[key]
return
}
func (s *SafeMap[K, V]) Set(key K, value V) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}
func (s *SafeMap[K, V]) Delete(key K) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.m, key)
}
func (s *SafeMap[K, V]) Has(key K) bool {
s.mu.RLock()
defer s.mu.RUnlock()
_, ok := s.m[key]
return ok
}
func (s *SafeMap[K, V]) Len() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.m)
}
func (s *SafeMap[K, V]) Clear() {
s.mu.Lock()
defer s.mu.Unlock()
s.m = make(map[K]V)
}
func (s *SafeMap[K, V]) Keys() []K {
s.mu.RLock()
defer s.mu.RUnlock()
keys := make([]K, 0, len(s.m))
for k := range s.m {
keys = append(keys, k)
}
return keys
}
func (s *SafeMap[K, V]) Values() []V {
s.mu.RLock()
defer s.mu.RUnlock()
values := make([]V, 0, len(s.m))
for _, v := range s.m {
values = append(values, v)
}
return values
}
func (s *SafeMap[K, V]) ForEach(fn func(K, V)) {
s.mu.RLock()
defer s.mu.RUnlock()
for k, v := range s.m {
fn(k, v)
}
}
package collection
// Contains returns true if the given item is present in the slice.
func Contains[T comparable](source []T, item T) bool {
for _, v := range source {
if v == item {
return true
}
}
return false
}
// MapContains returns true if the given key is present in the map.
func MapContains[K comparable, T any](source map[K]T, item K) bool {
_, ok := source[item]
return ok
}
// Any: Returns true if at least one element in the slice satisfies the given predicate function.
func Any[T any](source []T, predicate func(T) bool) bool {
for _, v := range source {
if predicate(v) {
return true
}
}
return false
}
// All: Returns true if every element in the slice satisfies the given predicate function.
func All[T any](source []T, predicate func(T) bool) bool {
for _, v := range source {
if !predicate(v) {
return false
}
}
return true
}
package collection
import (
"sort"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
// Sort sorts the source slice of type T in ascending order.
func Sort[T constraints.Ordered](source []T) {
slices.Sort(source)
}
// SortBy sorts the source slice of type T according to the less function provided.
func SortBy[T any](source []T, less func(l T, r T) bool) {
sort.Slice(source, func(i, j int) bool {
return less(source[i], source[j])
})
}
// Reverse reverses the order of the elements in the source slice of type T.
func Reverse[T any](source []T) {
for i, j := 0, len(source)-1; i < j; i, j = i+1, j-1 {
source[i], source[j] = source[j], source[i]
}
}
package collection
import (
"context"
"errors"
"sync"
)
// TransformBy transform the source slice of type T to a new slice of type K using the provided transform function.
func TransformBy[T, K any](source []T, transform func(T) K) []K {
var result = make([]K, len(source))
for i, item := range source {
result[i] = transform(item)
}
return result
}
// TransformManyBy transforms the source slice of type T to multiple slices of type K using the provided transform function.
func TransformManyBy[T, K any](source []T, transform func(T) []K) []K {
var many = TransformBy(source, transform)
return Flatten(many)
}
// TryTransformBy tries to transform the source slice of type T to a new slice of type K using the provided transform function.
func TryTransformBy[T, K any](source []T, transform func(T) (K, error)) ([]K, error) {
var result = make([]K, len(source))
for i, item := range source {
var value, err = transform(item)
if err != nil {
return nil, err
}
result[i] = value
}
return result, nil
}
// MapTransformBy transform the values of the source map of type T1 to a new map of type T2 using the provided transform function.
func MapTransformBy[K comparable, T1, T2 any](source map[K]T1, transform func(T1) T2) map[K]T2 {
var result = make(map[K]T2, len(source))
for k, v := range source {
result[k] = transform(v)
}
return result
}
// TryMapTransformBy attempts to transform the values of the source map of type T1 to a new map of type T2 using the provided transform function.
func TryMapTransformBy[K comparable, T1, T2 any](source map[K]T1, transform func(T1) (T2, error)) (map[K]T2, error) {
var result = make(map[K]T2, len(source))
for k, v := range source {
var value, err = transform(v)
if err != nil {
return nil, err
}
result[k] = value
}
return result, nil
}
// MapToSlice convert the source map of type T1 to a slice of type T2 using the provided transform function on each key-value pair.
func MapToSlice[K comparable, T1 any, T2 any](source map[K]T1, transform func(key K, value T1) T2) []T2 {
var result = make([]T2, 0, len(source))
for key, value := range source {
result = append(result, transform(key, value))
}
return result
}
// SliceToMap convert the source slice of type T to a new map of type T with keys generated by the provided keyFunc.
func SliceToMap[K comparable, T any](source []T, keyFunc func(T) K) map[K]T {
var result = make(map[K]T, len(source))
for _, v := range source {
result[keyFunc(v)] = v
}
return result
}
// Flatten flattens a slice of slices into a single slice.
func Flatten[T any](source [][]T) []T {
var size int
for _, v := range source {
size += len(v)
}
var result = make([]T, 0, size)
for _, v := range source {
result = append(result, v...)
}
return result
}
// Duplicates returns a new slice with all elements that appear more than once in the original slice.
func Duplicates[T comparable](source []T) []T {
var visited = make(map[T]struct{}, len(source))
var result = FilterBy(source, func(v T) bool {
if _, ok := visited[v]; ok {
return true
}
visited[v] = struct{}{}
return false
})
return Distinct(result)
}
// ChunkBy divides a slice of Type T into smaller chunks of the specified size.
func ChunkBy[T any](source []T, size int) [][]T {
if size <= 0 || len(source) == 0 {
return nil
}
var chunks = make([][]T, 0, len(source)/size)
for i := 0; i < len(source); i += size {
var end = i + size
if end > len(source) {
end = len(source)
}
chunks = append(chunks, source[i:end])
}
return chunks
}
// AsyncTransformBy async transform the source slice of type T to a new slice of type K using the provided transform function.
func AsyncTransformBy[T, K any](source []T, transform func(T) K) []K {
var results = make([]K, len(source))
var wg sync.WaitGroup
wg.Add(len(source))
for i, item := range source {
go func(i int, item T) {
defer wg.Done()
results[i] = transform(item)
}(i, item)
}
wg.Wait()
return results
}
// AsyncTryTransformBy tries to async transform the source slice of type T to a new slice of type K using the provided transform function.
func AsyncTryTransformBy[T, K any](parent context.Context, source []T, transform func(context.Context, T) (K, error)) ([]K, error) {
var (
resultsCh = make(chan Pair[K, error], len(source))
ctx, cancel = context.WithCancel(parent)
)
defer cancel()
var wg sync.WaitGroup
wg.Add(len(source))
go func() {
wg.Wait()
close(resultsCh)
}()
for _, item := range source {
go func(item T) {
defer wg.Done()
var response, err = transform(ctx, item)
resultsCh <- Pair[K, error]{First: response, Second: err}
}(item)
}
var (
result = make([]K, 0, len(source))
errs []error
)
for r := range resultsCh {
if r.Second != nil {
errs = append(errs, r.Second)
cancel()
continue
}
result = append(result, r.First)
}
if err := errors.Join(errs...); err != nil {
return nil, err
}
return result, nil
}