The design is inspired by samber/lo and iterator proposal. This library does not deal with channel/pipes/concurrency as that is beyond the scope of this project.
Root package github.com/rprtr258/fun
provides common slice and functional utilities.
// Pair is a data structure that has two values.
type Pair[K, V any] struct {K K; V V}
// Option is either value or nothing.
type Option[T any] struct {Value T; Valid bool}
// Result is either value or error.
type Result[T any] Pair[T, error]
// RealNumber is a generic number interface that covers all Go real number types.
type RealNumber interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
// Number is a generic number interface that covers all Go number types.
type Number interface {
RealNumber | complex64 | complex128
}
Declarations like
func Map[R, T any, F interface {
func(T) R | func(T, int) R
}](f F, slice ...T) []R
exists for the reason that we want both func(elem)
and func(elem, index)
functions work. With such declaration Go cannot infer type R
, so we have to specify it explicitly on usage: fun.Map[string](fn, slice...)
Another moment is that slice arguments are variadic. That allows user not to construct slice in some cases like fun.Contains(status, "OK", "Success")
instead of fun.Contains(status, []string{"OK", "Success"})
.
Applies function to all elements and returns slice with results.
fun.Map(func(x int64, _ int) string {
return strconv.FormatInt(x, 10)
}, 0, 1, 2)
// []string{"0", "1", "2"}
Filters slice elements using given predicate.
fun.Filter(func(x int64, _ int) bool {
return x%2 == 0
}, 0, 1, 2)
// []int64{0, 2}
Transform each element, leaving only those for which true is returned.
fun.FilterMap(func(x int64, _ int) (string, bool) {
return strconv.FormatInt(x, 10), x%2 == 0
}, 0, 1, 2)
// []string{"0", "2"}
Like Map
but uses dictionary instead of transform function.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.MapDict(dict, 0, 1, 2)
// []string{"zero", "one", "two"}
Like Map
but returns first error got from transform.
fun.MapErr(func(x int64, _ int) (string, error) {
if x%2 == 0 {
return strconv.FormatInt(x, 10), nil
}
return "", errors.New("odd")
}, 0, 1, 2)
// []string{"0"}, errors.New("odd")
Transforms map to slice using transform function. Order is not guaranteed.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.MapToSlice(dict, func(k int, v string) string {
return fmt.Sprintf("%d: %s", k, v)
})
// []string{"0: zero", "1: one", "2: two"}
Transforms map to slice using transform function and returns only those for which true is returned. Order is not guaranteed.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.MapFilterToSlice(dict, func(k int, v string) (string, bool) {
return fmt.Sprintf("%d: %s", k, v), k%2 == 0
})
// []string{"0: zero", "2: two"}
Returns keys of map. Order is not guaranteed.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.Keys(dict)
// []int{0, 1, 2}
Returns values of map. Order is not guaranteed.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.Values(dict)
// []string{"zero", "one", "two"}
Returns the key of the first element predicate returns truthy for.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.FindKeyBy(dict, func(k int, v string) bool {
return v == "zero"
})
// 0, true
Returns unique values of slice. In other words, removes duplicates.
fun.Uniq(1, 2, 3, 1, 2)
// []int{1, 2, 3}
Returns first found element by predicate along with it's index.
fun.Index(func(s string, _ int) bool {
return strings.HasPrefix(s, "o")
}, "zero", "one", "two")
// "one", 1, true
Returns true if an element is present in a collection.
fun.Contains("zero", "zero", "one", "two")
// true
Returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
fun.SliceToMap(func(x int, _ int) (int, int) {
return x, x * 10
}, 0, 1, 2)
// map[int]int{0: 0, 1: 10, 2: 20}
Returns slice of key/value pairs from map.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.FromMap(dict)
// []fun.Pair[int, string]{0: "zero", 1: "one", 2: "two"}
Returns copy of slice.
fun.Copy(1, 2, 3)
// []int{1, 2, 3}
Reverses slice in place.
xs := []int{1, 2, 3}
fun.ReverseInplace(xs)
// xs becomes []int{3, 2, 1}
Returns slice from start to end without panicking on out of bounds.
xs := []int{1, 2, 3, 4, 5}
fun.Subslice(1, 4, xs...)
// []int{2, 3, 4}
Divides slice into chunks of size chunkSize.
xs := []int{1, 2, 3, 4, 5}
fun.Chunk(2, xs...)
// [][]int{{1, 2}, {3, 4}, {5}}
Like Map
but concatenates results.
fun.ConcatMap(func(x int) []int {
return []int{x, x + 10, x + 100}
}, 0, 1, 2)
// []int{0, 10, 100, 1, 11, 101, 2, 12, 102}
Returns true if all elements satisfy the condition.
fun.All(func(x int) bool {
return x%2 == 0
}, 0, 2, 4)
// true
Returns true if any (at least one) element satisfies the condition.
fun.Any(func(x int) bool {
return x%2 == 0
}, 0, 1, 2)
// true
Sorts slice in place by given function.
xs := []int{1, 2, 3, 4, 5}
fun.SortBy(func(x int) int {
return -x
}, xs)
// xs becomes []int{5, 4, 3, 2, 1}
Groups elements by key.
fun.GroupBy(func(x int) int {
return x % 2
}, 0, 1, 2, 3, 4)
// map[int][]int{0: {0, 2, 4}, 1: {1, 3}}
Utilities utilizing values comparison.
Returns the minimum of the given values.
fun.Min(1, 2, 3)
// 1
Returns the maximum of the given values.
fun.Max(1, 2, 3)
// 3
Returns x clamped between low and high.
fun.Clamp(99, 1, 10)
// 10
Returns first minimum of given values using given order function.
fun.MinBy(func(s string) int {
return len(s)
}, "one", "two", "three")
// "one"
Returns first maximum of given values using given order function.
fun.MaxBy(func(s string) int {
return len(s)
}, "one", "two", "three")
// "three"
Returns empty Option.
fun.Invalid[int]()
// Option[int]{}
Returns Option with given value.
fun.Valid(1)
// Option[int]{Value: 1, Valid: true}
Returns Option with given value and validity.
fun.Optional(1, true)
// Option[int]{Value: 1, Valid: true}
Returns Option with value from pointer.
x := 1
fun.FromPtr(&x)
// Option[int]{Value: 1, Valid: true}
fun.FromPtr[int](nil)
// Option[int]{}
Returns value and validity.
fun.Valid(1).Unpack()
// (1, true)
Returns first valid Option.
fun.Valid(1).Or(fun.Invalid[int]())
// Option[int]{Value: 1, Valid: true}
Returns value if Option is valid, otherwise returns default value.
fun.Valid(1).OrDefault(0)
// 1
Returns pointer to value if Option is valid, otherwise returns nil.
fun.Valid(1).Ptr()
// &[]int{1}[0]
Returns new Option with transformed value.
fun.Valid(1).OptMap(func(x int) string {
return fmt.Sprintf("%d", x)
})
// Option[string]{Value: "1", Valid: true}
Returns new Option with transformed optional value.
fun.Valid(1).OptFlatMap(func(x int) Option[string] {
return fun.Valid(fmt.Sprintf("%d", x))
})
// Option[string]{Value: "1", Valid: true}
Returns zero value of given type.
fun.Zero[int]()
// 0
Prints value and returns it. Useful for debug printing.
fun.Debug(2+2)*2
// prints 4
Returns true if map has such key.
dict := map[int]string{
0: "zero",
1: "one",
2: "two",
}
fun.Has(dict, 2)
// true
Returns first value for which true is returned.
fun.Cond(
1,
func() (int, bool) { return 2, true },
func() (int, bool) { return 3, false },
)
// 2
Returns pointer to value.
fun.Ptr(1)
// &[]int{1}[0]
Returns value from pointer. If pointer is nil returns zero value.
fun.Deref[int](nil) // 0
fun.Deref[int](new(int)) // 0
x := 1
fun.Deref[int](&x) // 1
Returns value after applying endomorphisms. Endomorphism is just function from type to itself.
fun.Pipe(
"hello ",
strings.TrimSpace,
strings.NewReplacer("l", "|").Replace,
strings.ToUpper,
)
// "HE||O"
There are multiple variations of if
statement usable as expression.
Simple ternary function.
fun.IF(true, 1, 0)
// 1
Returns value from branch for which predicate is true. F
suffix can be used to get values not evaluated immediately.
fun.If(true, 1).Else(0)
// 1
fun.If(false, 1).ElseF(func() int { return 0 })
// 0
fun.If(false, 1).ElseIf(true, 2).Else(3)
// 2
fun.IfF(false, func() int { return 1 }).Else(0)
// 0
fun.If(true, db.Get(0)).Else(db.Get(1))
// db.Get(0) result, db.Get called two times
fun.IfF(true, func() Thing { return db.Get(0) }).ElseF(func() Thing { return db.Get(1) })
// db.Get(0) result, db.Get called once
switch
usable as expression.
fun.Switch("one", -1).
Case("zero", 0).
Case("one", 1).
Case("two", 2).
End()
// 1
github.com/rprtr258/fun/iter
introduces iterator primitives for which iter.Seq[T]
is basic.
type Seq[V any] func(yield func(V) bool)
Which is a function which accepts function to yield
values from iteration. yield
must return false
when iteration must stop (analogous to break
).
Example iterator yielding numbers from 1 to n
, including n
:
func Range(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := range n {
if !yield(i) {
return
}
}
}
}
github.com/rprtr258/fun/set
introduces Set[T]
primitive for collections of unique comparable
values.
github.com/rprtr258/fun/orderedmap
introduces OrderedMap[K, V]
data structure which acts like hashmap but also allows to iterate over keys in sorted order. Internally, binary search tree is used.