Skip to content

A practical Go toolkit with generic utilities for working with slices, maps, and functional programming primitives like Option and Result.

License

Notifications You must be signed in to change notification settings

sonirico/stadio

Repository files navigation

Stadio

Go Report Card Go Reference License: MIT

Stadio Art

The ultimate toolkit for Go developers. A comprehensive collection of functions, data structures, and utilities designed to enhance productivity and code quality.

Modules

Table of Contents

🪄 Fp

Package fp provides functional programming primitives for Go. It implements monadic types like Option and Result for more expressive error handling.

Functions

Err

Err creates a new Result in the error state with the given error. This is a constructor function for creating a Result that represents failure.

Code
func Err[T any](err error) Result[T] {
	return Result[T]{err: err}
}

None

None creates a new Option in the None state. This is a constructor function for creating an Option that does not contain a value.

Code
func None[T any]() Option[T] {
	return Option[T]{}
}

Ok

Ok creates a new Result in the Ok state with the given value. This is a constructor function for creating a Result that represents success.

Code
func Ok[T any](v T) Result[T] {
	return Result[T]{value: v, err: nil}
}

OkZero

OkZero creates a new Result in the Ok state with the zero value. This is a constructor function for creating a Result that represents success but doesn't carry a meaningful value.

Code
func OkZero[T any]() Result[T] {
	return Result[T]{err: nil}
}

OptionFromPtr

OptionFromPtr creates an Option from a pointer. If the pointer is nil, returns None, otherwise returns Some with the dereferenced value. This is useful for converting nullable pointers to the Option type.

Code
func OptionFromPtr[T any](x *T) Option[T] {
	if x == nil {
		return None[T]()
	}
	return Some(*x)
}

OptionFromTuple

OptionFromTuple creates an Option from a tuple-like return (value, ok). If ok is true, returns Some(x), otherwise returns None. This is useful for converting Go's common (value, ok) pattern to an Option.

Code
func OptionFromTuple[T any](x T, ok bool) Option[T] {
	if ok {
		return Some(x)
	}
	return None[T]()
}

OptionFromZero

OptionFromZero creates an Option from a value, treating zero values as None. If the value equals the zero value for its type, returns None, otherwise returns Some(x). This is useful when zero values are treated as invalid or unset.

Code
func OptionFromZero[T comparable](x T) Option[T] {
	var zero T
	if x == zero {
		return None[T]()
	}
	return Some(x)
}

Some

Some creates a new Option in the Some state with the given value. This is a constructor function for creating an Option that contains a value.

Code
func Some[T any](t T) Option[T] {
	return Option[T]{value: t, isSome: true}
}

⬆️ Back to Top


🗝️ Maps

Package maps provides generic utility functions to work with Go maps. It offers a functional approach to common map operations like filtering, mapping, reducing, and comparing maps.

Functions

Equals

Equals compares two maps and returns whether they are equal in values. Two maps are considered equal if:

  • They have the same length
  • They contain the same keys
  • For each key, the values in both maps satisfy the equality function

Maps are compared using the provided equality function for values. This allows for deep equality checks on complex value types.

Code
func Equals[K comparable, V any](m1, m2 map[K]V, eq func(V, V) bool) bool {
	if len(m1) != len(m2) {
		return false
	}

	if m1 == nil && m2 != nil {
		return false
	}

	if m1 != nil && m2 == nil {
		return false
	}

	for k1, v1 := range m1 {
		v2, ok := m2[k1]
		if !ok {
			return false
		}

		if !eq(v1, v2) {
			return false
		}
	}

	return true
}

Filter

Filter creates a new map containing only the key-value pairs that satisfy the predicate. The predicate function takes a key and value and returns a boolean indicating whether to include the entry in the result.

Unlike FilterInPlace, this function creates a new map and does not modify the input map.

Code
func Filter[K comparable, V any](
	m map[K]V,
	p func(K, V) bool,
) map[K]V {
	if m == nil {
		return nil
	}

	res := make(map[K]V, len(m))

	for k, v := range m {
		if p(k, v) {
			res[k] = v
		}
	}

	return res
}

FilterInPlace

FilterInPlace modifies the given map by removing entries that do not satisfy the predicate. The predicate function takes a key and value and returns a boolean indicating whether to keep the entry in the map.

This function directly modifies the input map for better performance when creating a new map is not necessary. It returns the modified map for convenience in chaining operations.

Code
func FilterInPlace[K comparable, V any](
	m map[K]V,
	p func(K, V) bool,
) map[K]V {
	if m == nil {
		return nil
	}

	for k, v := range m {
		if !p(k, v) {
			delete(m, k)
		}
	}

	return m
}

FilterMap

FilterMap both filters and maps a map into a new map, potentially with different key and value types. The predicate function should return an fp.Option monad containing a tuple of the new key and value:

  • fp.Some to include the entry in the result (with transformed key and value)
  • fp.None to exclude the entry from the result

This provides a powerful way to simultaneously transform and filter map entries while leveraging the Option monad for expressing presence/absence.

Code
func FilterMap[K1 comparable, V1 any, K2 comparable, V2 any](
	m map[K1]V1,
	p func(K1, V1) fp.Option[tuples.Tuple2[K2, V2]],
) map[K2]V2 {
	if m == nil {
		return nil
	}

	res := make(map[K2]V2, len(m))

	for k1, v1 := range m {
		tpl := p(k1, v1)
		if tpl.IsSome() {
			v := tpl.UnwrapUnsafe()
			res[v.V1] = v.V2
		}
	}

	return res
}

FilterMapTuple

FilterMapTuple both filters and maps the given map into a new map, potentially with different key and value types. The predicate function returns three values:

  • The new key (K2)
  • The new value (V2)
  • A boolean indicating whether to include this entry in the result

This function is an alternative to FilterMap that uses Go's native boolean return instead of the Option monad for expressing presence/absence.

Code
func FilterMapTuple[K1 comparable, V1 any, K2 comparable, V2 any](
	m map[K1]V1,
	p func(K1, V1) (K2, V2, bool),
) map[K2]V2 {
	if m == nil {
		return nil
	}

	res := make(map[K2]V2, len(m))

	for k1, v1 := range m {
		if k2, v2, ok := p(k1, v1); ok {
			res[k2] = v2
		}
	}

	return res
}

Fold

Fold compacts a map into a single value by iteratively applying a reduction function with an explicit initial value. The reduction function takes the accumulator, a key, and a value, and returns the updated accumulator.

Unlike Reduce, Fold takes an explicit initial value for the accumulator. This is useful when the zero value of the result type is not appropriate as the starting value.

Code
func Fold[K comparable, V any, R any](
	m map[K]V,
	p func(R, K, V) R,
	initial R,
) R {
	if m == nil {
		return initial
	}

	r := initial

	for k, v := range m {
		r = p(r, k, v)
	}

	return r
}

Map

Map transforms a map into another map, with potentially different key and value types. The transformation is applied to each key-value pair by the provided function, which returns the new key and value for the resulting map.

This function preserves nil semantics: if the input map is nil, the output will also be nil. Otherwise, a new map is created with the transformed key-value pairs.

Code
func Map[K1 comparable, V1 any, K2 comparable, V2 any](
	m map[K1]V1,
	p func(K1, V1) (K2, V2),
) map[K2]V2 {
	if m == nil {
		return nil
	}

	res := make(map[K2]V2, len(m))

	for k1, v1 := range m {
		k2, v2 := p(k1, v1)
		res[k2] = v2
	}

	return res
}

Reduce

Reduce compacts a map into a single value by iteratively applying a reduction function. The reduction function takes the accumulator, a key, and a value, and returns the updated accumulator.

The initial value for the accumulator is the zero value of type R. If you need a different initial value, use Fold instead.

Code
func Reduce[K comparable, V any, R any](
	m map[K]V,
	p func(R, K, V) R,
) R {
	var r R

	if m == nil {
		return r
	}

	for k, v := range m {
		r = p(r, k, v)
	}

	return r
}

Slice

Slice converts a map into a slice by applying a transformation function to each key-value pair. The transformation function takes a key and value and returns an element for the resulting slice.

The order of elements in the resulting slice is not guaranteed, as map iteration in Go is not deterministic.

Code
func Slice[K comparable, V, R any](
	m map[K]V,
	p func(K, V) R,
) slices.Slice[R] {
	res := make([]R, len(m))
	i := 0

	for k, v := range m {
		res[i] = p(k, v)
		i++
	}

	return res
}

⬆️ Back to Top


⛓️ Slices

Package slices provides a comprehensive set of generic utility functions for working with slices. It offers a functional approach to common slice operations such as transforming, filtering, searching, and manipulating elements in a type-safe manner.

Functions

All

All checks if all elements in the slice satisfy the predicate. Returns true if all elements match the predicate, false otherwise.

Code
func All[T any](arr []T, predicate func(t T) bool) bool {
	for _, x := range arr {
		if !predicate(x) {
			return false
		}
	}
	return true
}

Any

Any checks if at least one element in the slice satisfies the predicate. Returns true if any element matches the predicate, false otherwise. Alias for Contains.

Code
func Any[T any](arr []T, predicate func(t T) bool) bool {
	return Contains(arr, predicate)
}

Append

Append adds an element to the end of the slice and returns the result. Unlike the builtin append, this function is explicitly named for clarity.

Code
func Append[T any](arr []T, item T) []T {
	return append(arr, item)
}

AppendVector

AppendVector adds all elements from another slice to the end of this slice. Returns the resulting concatenated slice.

Code
func AppendVector[T any](arr, items []T) []T {
	return append(arr, items...)
}

Contains

Contains checks if the slice contains an element that satisfies the predicate. Returns true if any element matches the predicate, false otherwise.

Code
func Contains[T any](arr []T, predicate func(t T) bool) bool {
	return IndexOf(arr, predicate) >= 0
}

Cut

Cut removes a sector from slice given lower and upper bounds. Bounds are represented as indices of the slice. E.g: Cut([1, 2, 3, 4], 1, 2) -> [1, 4] Cut([4], 0, 0) -> [] Cut will returned the original slice without the cut subslice.

Code
func Cut[T any](arr []T, from, to int) []T {
	if len(arr) < 1 {
		return arr
	}

	if from < 0 {
		from = 0
	}

	if from >= len(arr) {
		from = len(arr) - 1
	}

	if to < 0 {
		to = 0
	}

	if to >= len(arr) {
		to = len(arr) - 1
	}

	if len(arr) == 1 {
		return arr[:0]
	}

	if from > to {

		return append(arr[:from], arr[from+to+1:]...)
	}

	return append(arr[:from], arr[to+1:]...)
}

Delete

Delete removes the element at the specified index without preserving order. This provides better performance than DeleteOrder but changes the order of elements. If the index is out of bounds, returns the original slice unchanged.

Code
func Delete[T any](arr []T, idx int) []T {
	le := len(arr) - 1
	if le < 0 || idx > le || idx < 0 {
		return arr
	}
	var t T
	arr[idx] = arr[le]
	arr[le] = t
	arr = arr[:le]
	return arr
}

DeleteOrder

DeleteOrder removes the element at the specified index while preserving order. This is slower than Delete but maintains the relative order of the remaining elements. If the index is out of bounds, returns the original slice unchanged.

Code
func DeleteOrder[T any](arr []T, idx int) []T {
	le := len(arr) - 1
	if le < 0 || idx > le || idx < 0 {
		return arr
	}
	var t T

	if le > 0 {
		copy(arr[idx:], arr[idx+1:])
	}

	arr[le] = t
	arr = arr[:le]
	return arr
}

Equals

Equals compares two slices and returns whether they contain equal elements. Two slices are considered equal if they have the same length and corresponding elements satisfy the equality function.

Code
func Equals[T any](one, other []T, predicate func(x, y T) bool) (res bool) {
	if len(one) != len(other) {
		return
	}

	res = true

	for idx, otherItem := range other {
		res = predicate(one[idx], otherItem)
		if !res {
			return
		}
	}
	return
}

Extract

Extract gets and deletes the first element that matches the predicate. Returns the modified slice, the extracted element, and a success flag. If no element matches, returns the original slice, zero value, and false.

Code
func Extract[T any](arr []T, predicate func(t T) bool) ([]T, T, bool) {
	res, idx := FindIdx(arr, predicate)
	if idx < 0 {
		return arr, res, false
	}

	arr = Delete(arr, idx)
	return arr, res, true
}

ExtractIdx

ExtractIdx gets and deletes the element at the given position. Returns the modified slice, the extracted element, and a success flag. If the index is out of bounds, returns the original slice, zero value, and false.

Code
func ExtractIdx[T any](arr []T, idx int) (res []T, item T, ok bool) {
	if idx >= len(arr) || idx < 0 {
		return
	}

	ok = true
	item = arr[idx]
	res = Delete(arr, idx)

	return
}

Filter

Filter creates a new slice containing only the elements that satisfy the predicate.

Code
func Filter[T any](arr []T, predicate func(t T) bool) []T {
	res := make([]T, 0, len(arr))

	for _, x := range arr {
		if predicate(x) {
			res = append(res, x)
		}
	}

	return res
}

FilterInPlace

FilterInPlace modifies the slice in place to contain only elements that satisfy the predicate. This is more efficient than Filter when creating a new slice is not necessary.

Code
func FilterInPlace[T any](arr []T, predicate func(t T) bool) []T {
	n := 0
	for i, x := range arr {
		if predicate(x) {
			if n != i {
				arr[n] = x
			}
			n++
		}
	}

	arr = arr[:n]

	return arr
}

FilterInPlaceCopy

FilterInPlaceCopy filters the slice in place and returns a copy of the result. This combines the efficiency of FilterInPlace with the safety of creating a new slice.

Code
func FilterInPlaceCopy[T any](arr []T, predicate func(t T) bool) []T {
	n := 0
	for i, x := range arr {
		if predicate(x) {
			if n != i {
				arr[n] = x
			}
			n++
		}
	}

	arr = arr[:n]

	res := make([]T, n)

	copy(res, arr[:n])

	return res
}

FilterMap

FilterMap creates a new slice by applying a transformation function that returns an Option. Elements with Some options are included in the result, while None options are excluded.

Code
func FilterMap[T, U any](arr []T, predicate func(t T) fp.Option[U]) []U {
	pre := func(t T) (U, bool) {
		return predicate(t).Unwrap()
	}

	return FilterMapTuple[T, U](arr, pre)
}

FilterMapTuple

FilterMapTuple creates a new slice by applying a transformation function that also filters elements. The function should return the transformed value and a boolean indicating whether to include the element.

Code
func FilterMapTuple[T, U any](arr []T, predicate func(t T) (U, bool)) []U {
	res := make([]U, 0, len(arr))

	for _, x := range arr {
		if mapped, ok := predicate(x); ok {
			res = append(res, mapped)
		}
	}

	return res
}

Find

Find returns the first element that satisfies the predicate. Returns the element and true if found, otherwise the zero value and false.

Code
func Find[T any](arr []T, predicate func(t T) bool) (res T, ok bool) {
	var idx int
	res, idx = FindIdx(arr, predicate)
	ok = idx > -1
	return
}

FindIdx

FindIdx returns the first element that satisfies the predicate and its index. Returns the element and its index if found, otherwise the zero value and -1.

Code
func FindIdx[T any](arr []T, predicate func(t T) bool) (res T, idx int) {
	idx = IndexOf(arr, predicate)
	if idx < 0 {
		return
	}

	res = arr[idx]
	return
}

Fold

Fold compacts the slice into a single value by iteratively applying the reduction function, starting with the provided initial value. The accumulator type can be different from the element type.

Code
func Fold[T, U any](arr []T, p func(U, T) U, initial U) U {
	if len(arr) < 1 {
		return initial
	}

	initial = p(initial, arr[0])

	if len(arr) < 2 {
		return initial
	}

	i := 1

	for i < len(arr) {
		initial = p(initial, arr[i])

		i++
	}

	return initial
}

FoldSame

FoldSame is a convenience wrapper around Fold for when the accumulator and element types are the same.

Code
func FoldSame[T any](arr []T, p func(T, T) T, initial T) T {
	return Fold[T, T](arr, p, initial)
}

Includes

Includes checks if the slice contains a specific element using the equality operator. Returns true if the element is found, false otherwise.

Code
func Includes[T comparable](arr []T, target T) bool {
	return Contains(arr, func(t T) bool {
		return t == target
	})
}

IndexOf

IndexOf returns the index of the first element that satisfies the predicate. Returns the index where the element was found, or -1 if not found.

Code
func IndexOf[T any](arr []T, predicate func(t T) bool) (pos int) {
	pos = -1
	for i, x := range arr {
		if predicate(x) {
			pos = i
			return
		}
	}
	return
}

Insert

Insert places an element at the specified index in the slice. Elements at or after the index are shifted to the right. Returns the resulting slice with the new element inserted. If the index is out of bounds, returns the original slice unchanged.

Code
func Insert[T any](arr []T, item T, idx int) []T {
	if arr == nil {
		return []T{item}
	}

	if idx < 0 || idx > len(arr) {
		return arr
	}

	return append(arr[:idx], append([]T{item}, arr[idx:]...)...)
}

InsertVector

InsertVector places a slice of elements at the specified index in the slice. Elements at or after the index are shifted to the right. Returns the resulting slice with the new elements inserted. If the index is out of bounds, returns the original slice unchanged.

Code
func InsertVector[T any](arr, items []T, idx int) (res []T) {
	if arr == nil {
		res = items[:]
		return
	}

	if items == nil || len(items) == 0 {
		res = arr
		return
	}

	if idx < 0 || idx > len(arr) {
		return arr
	}

	return append(arr[:idx], append(items, arr[idx:]...)...)
}

Map

Map creates a new slice by applying the transformation function to each element. The transformation can change the type of the elements.

Code
func Map[T, U any](arr []T, predicate func(t T) U) []U {
	res := make([]U, 0, len(arr))

	for _, x := range arr {
		res = append(res, predicate(x))
	}

	return res
}

MapInPlace

MapInPlace transforms each element in the slice using the provided function. Modifies the slice in place and returns it.

Code
func MapInPlace[T any](arr []T, predicate func(t T) T) []T {
	for i, x := range arr {
		arr[i] = predicate(x)
	}

	return arr
}

Peek

Peek returns the item at the specified index without modifying the slice. Returns the element and true if the index is valid, otherwise the zero value and false.

Code
func Peek[T any](arr []T, idx int) (item T, ok bool) {
	if len(arr) < 1 || idx >= len(arr) {
		return
	}

	item = arr[idx]
	ok = true

	return
}

Pop

Pop deletes and returns the last item from the slice. Returns the modified slice, the popped element, and a success flag. If the slice is empty, returns the original slice, zero value, and false.

Code
func Pop[T any](arr []T) (res []T, item T, ok bool) {
	if len(arr) < 1 {
		return
	}

	var t T
	le := len(arr) - 1
	res = arr[:le]
	item = arr[le]
	ok = true

	arr[le] = t

	return
}

PopFront

PopFront removes and returns the first element of the slice. Returns the modified slice (without the first element), the removed element, and a success flag. If the slice is empty, returns the original slice, zero value, and false.

Code
func PopFront[T any](arr []T) (res []T, item T, ok bool) {
	if len(arr) < 1 {
		res = arr
		return
	}

	item, res = arr[0], arr[1:]
	return
}

PushFront

PushFront inserts an element at the beginning of the slice. Returns the resulting slice with the new element at the front.

Code
func PushFront[T any](arr []T, item T) []T {
	return append([]T{item}, arr...)
}

Reduce

Reduce compacts the slice into a single value by iteratively applying the reduction function to each element. Starts with the zero value.

Code
func Reduce[T, U any](arr []T, p func(T, T) T) (res T) {
	return Fold(arr, p, res)
}

ReduceSame

ReduceSame is a convenience wrapper around Reduce for when the accumulator and element types are the same.

Code
func ReduceSame[T any](arr []T, p func(T, T) T) T {
	return Reduce[T, T](arr, p)
}

Shift

Shift removes and returns the first element of the slice. Alias for PopFront, following JavaScript array method naming conventions.

Code
func Shift[T any](arr []T) ([]T, T, bool) {
	return PopFront(arr)
}

Some

Some checks if at least one element in the slice satisfies the predicate. Returns true if any element matches the predicate, false otherwise. Alias for Contains.

Code
func Some[T any](arr []T, predicate func(t T) bool) bool {
	return Contains(arr, predicate)
}

ToMap

ToMap creates a map from a slice, using the provided function to determine the key for each element. The element itself becomes the value in the map.

Code
func ToMap[V any, K comparable](arr []V, predicate func(x V) K) map[K]V {
	res := make(map[K]V, len(arr))

	for _, x := range arr {
		res[predicate(x)] = x
	}

	return res
}

ToMapIdx

ToMapIdx creates a map from a slice, preserving each element's original index. Uses the provided function to determine the key for each element. The value in the map is a WrappedIdx containing both the element and its original index.

Code
func ToMapIdx[V any, K comparable](arr []V, predicate func(x V) K) map[K]WrappedIdx[V] {
	res := make(map[K]WrappedIdx[V], len(arr))

	for i, x := range arr {
		res[predicate(x)] = WrappedIdx[V]{value: x, idx: i}
	}

	return res
}

Unshift

Unshift inserts an element at the beginning of the slice. Alias for PushFront, following JavaScript array method naming conventions.

Code
func Unshift[T any](arr []T, item T) []T {
	return PushFront(arr, item)
}

⬆️ Back to Top


About

A practical Go toolkit with generic utilities for working with slices, maps, and functional programming primitives like Option and Result.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published