Awesome
gofn - Utility functions for Go 1.18+
This is a collection of generics utility functions for Go 1.18+.
Functionalities
gofn
consists of useful and convenient functions for most common needs when working on slices, maps, structs, transformation, conversion, and so on.
Try related libs:
- go-deepcopy: true deep copy function
Installation
go get github.com/tiendc/gofn
Function list
Slice
- Equal / EqualBy
- ContentEqual / ContentEqualBy
- Sort / IsSorted
- RemoveAt
- FastRemoveAt
- Remove
- FastRemove
- RemoveLastOf
- FastRemoveLastOf
- RemoveAll
- Replace / ReplaceN / ReplaceAll
- Splice / SpliceEx
- Concat
- Fill
- SliceByRange
- First / FirstOr
- Last / LastOr
Slice searching
- Contain
- ContainAll
- ContainAny
- Find / FindEx
- FindLast / FindLastEx
- IndexOf / IndexOfBy
- LastIndexOf / LastIndexOfBy
- CountValue / CountValueBy
Slice filtering
- Filter
- FilterNE
- FilterLT / FilterLTE
- FilterGT / FilterGTE
- FilterGT / FilterGTE
- FilterIN / FilterNIN
- FilterLIKE / FilterILIKE
Slice iteration
Slice uniqueness
- IsUnique / IsUniqueBy
- FindUniques / FindUniquesBy
- FindDuplicates / FindDuplicatesBy
- ToSet / ToSetBy / ToSetByReverse
Slice transformation
Slice algo
- Compact
- Drop
- Reverse / ReverseCopy
- Shuffle
- Chunk / ChunkByPieces
- Union / UnionBy
- Intersection / IntersectionBy
- Difference / DifferenceBy
- Reduce / ReduceEx
- ReduceReverse / ReduceReverseEx
- Partition / PartitionN
- Flatten / Flatten3
- Zip / Zip<N>
Slice conversion
Slice subset
Map
- MapEqual / MapEqualBy
- MapContainKeys
- MapContainValues
- MapIter
- MapKeys
- MapValues
- MapEntries
- MapGet
- MapPop
- MapSetDefault
- MapUpdate / MapUpdateExistingOnly / MapUpdateNewOnly
- MapCopy
- MapPick
- MapOmit
- MapOmitCopy
- MapReverse
Struct
String
- RuneLength
- RandString / RandStringEx
- StringJoin / StringJoinEx / StringJoinBy
- StringLexJoin / StringLexJoinEx
- StringWrap / StringUnwrap
Number
- ParseInt / ParseUint / ParseFloat
- FormatInt / FormatUint / FormatFloat
- NumberFmtGroup
- ToSafeInt<N> / ToSafeUint<N>
Time
Math
Concurrency
Function
Randomization
Error handling
Utility
Slice
Equal / EqualBy
Returns true if 2 slices have the same size and all elements of them equal in the current order.
Equal([]int{1, 2, 3}, []int{1, 2, 3}) // true
Equal([]int{1, 2, 3}, []int{3, 2, 1}) // false
// Use EqualBy for custom equal comparison
EqualBy([]string{"one", "TWO"}, []string{"ONE", "two"}, strings.EqualFold) // true
ContentEqual / ContentEqualBy
Returns true if 2 slices have the same size and contents equal regardless of the order of elements.
ContentEqual([]int{1, 2, 3}, []int{2, 1, 3}) // true
ContentEqual([]int{1, 2, 2, 3}, []int{2, 1, 3}) // false
ContentEqual([]int{1, 2, 2, 3}, []int{2, 1, 2, 3}) // true
ContentEqual([]int{1, 2, 2, 3}, []int{1, 1, 2, 3}) // false
// Use ContentEqualBy for custom key function
ContentEqualBy([]string{"one", "TWO"}, []string{"two", "ONE"}, strings.ToLower) // true
RemoveAt
Removes element at the specified index.
s := []int{1, 2, 3}
RemoveAt(&s, 1) // s == []int{1, 3}
FastRemoveAt
Removes element at the specified index by swapping it with the last element of the slice. This function is fast as it doesn't cause copying of slice content.
s := []int{1, 2, 3, 4}
FastRemoveAt(&s, 1) // s == []int{1, 4, 3} (2 and 4 are exchanged)
Sort / IsSorted
Convenient wrappers of the built-in sort.Slice
.
Sort([]int{1, 3, 2}) // []int{1, 2, 3}
SortDesc([]int{1, 3, 2}) // []int{3, 2, 1}
IsSorted([]int{1, 3, 2}) // false
IsSortedDesc([]int{3, 2, 1}) // true
Remove
Removes a value from a slice.
s := []int{1, 2, 3}
Remove(&s, 1) // s == []int{2, 3}
FastRemove
Removes a value from a slice by swapping it with the last element of the slice.
s := []int{1, 2, 3, 4}
FastRemove(&s, 2) // s == []int{1, 4, 3} (2 and 4 are exchanged)
RemoveLastOf
Removes last occurrence of a value from a slice.
s := []int{1, 2, 1, 3}
RemoveLastOf(&s, 1) // s == []int{1, 2, 3}
FastRemoveLastOf
Removes last occurrence of a value from a slice by swapping it with the last element of the slice.
s := []int{1, 2, 1, 3, 4}
FastRemoveLastOf(&s, 1) // s == []int{1, 2, 4, 3} (1 and 4 are exchanged)
RemoveAll
Removes all occurrences of a value from a slice.
s := []int{1, 2, 1, 3, 1}
RemoveAll(&s, 1) // s == []int{2, 3}
Replace / ReplaceN / ReplaceAll
Replaces a value with another value.
// Replaces first occurrence of the value
Replace([]int{1, 2, 1, 3, 1}, 1, 11) // []int{11, 2, 1, 3, 1}
// Replaces first n occurrences of the value (use -1 to replace all)
ReplaceN([]int{1, 2, 1, 3, 1}, 1, 11, 2) // []int{11, 2, 11, 3, 1}
// Replaces all occurrences of the value
ReplaceAll([]int{1, 2, 1, 3, 1}, 1, 11) // []int{11, 2, 11, 3, 11}
Splice / SpliceEx
Removes a portion of the given slice and inserts elements of another slice into that position.
Usage: Splice(s []T, start int, deleteCount int, insertingElements ...T)
.
If start < -len(s)
, 0
is used.
If -len(s) <= start < 0
, start + len(s)
is used.
If start >= len(s)
, no element will be deleted, but the new elements are still added to the end.
If deleteCount <= 0
, no elements will be removed.
s := Splice([]int{1, 2, 3}, 1, 1, 100) // s == []int{1, 100, 3}
s := Splice([]int{1, 2, 3}, 1, 0, 100) // s == []int{1, 100, 2, 3}
s := Splice([]int{1, 2, 3}, 1, 1, 100, 101) // s == []int{1, 100, 101, 3}
s := Splice([]int{1, 2, 3}, 0, 2, 100, 101) // s == []int{100, 101, 3}
// Use SpliceEx to get deleted items as the 2nd returning value
s, delItems := SpliceEx([]int{1, 2, 3}, 1, 1, 100) // s == []int{1, 100, 3}, delItems == []int{2}
s, delItems := SpliceEx([]int{1, 2, 3}, 1, 0, 100) // s == []int{1, 100, 2, 3}, delItems == []int{}
s, delItems := SpliceEx([]int{1, 2, 3}, 1, 1, 100, 101) // s == []int{1, 100, 101, 3}, delItems == []int{2}
s, delItems := SpliceEx([]int{1, 2, 3}, 0, 2, 100, 101) // s == []int{100, 101, 3}, delItems == []int{1, 2}
Concat
Concatenates two or more slices.
Concat([]int{1}, []int{2}, []int{2, 3}) // []int{1, 2, 2, 3}
Fill
Fills a slice with specified value.
s := make([]int, 5)
Fill(s, 1) // s == []int{1, 1, 1, 1, 1}
s2 := s[2:4]
Fill(s2, 1) // s2 == []int{1, 1}, s == []int{0, 0, 1, 1, 0}
First / FirstOr
Returns the first element of slice.
First([]int{1, 2, 3}) // 1, true
First([]string{}) // "", false
// Return the default value if slice is empty
FirstOr([]int{1, 2, 3}, 4) // 1
FirstOr([]int{}, 11) // 11
Last / LastOr
Returns the last element of slice.
Last([]int{1, 2, 3}) // 3, true
Last([]string{}) // "", false
// Return the default value if slice is empty
LastOr([]int{1, 2, 3}, 4) // 3
LastOr([]int{}, 11) // 11
SliceByRange
Generates a slice for the given range.
s := SliceByRange(0, 5, 1) // []int{0, 1, 2, 3, 4}
s := SliceByRange(0.0, 5, 2) // []float64{0, 2, 4}
s := SliceByRange(int32(5), 0, -2) // []int32{5, 3, 1}
Slice searching
Contain / ContainBy
Returns true if a slice contains a value.
Contain([]int{1, 2, 3}, 2) // true
Contain([]int{1, 2, 3}, 0) // false
// Use ContainBy for custom function
ContainBy([]string{"one", "TWO"}, func(elem string) bool {
return strings.ToLower(elem) == "two"
}) // true
ContainAll
Returns true if a slice contains all given values.
ContainAll([]int{1, 2, 3, 4, 5}, 2, 4, 3) // true
ContainAll([]int{1, 2, 3, 4, 5}, 2, 7) // false
ContainAny
Returns true if a slice contains any of the given values.
ContainAny([]int{1, 2, 3, 4, 5}, 2, 4, 7) // true
ContainAny([]int{1, 2, 3, 4, 5}, 7, 8, 9) // false
Find / FindEx
Finds a value in a slice.
v, found := Find([]string{"one", "TWO"}, func(elem string) bool {
return strings.ToLower(elem) == "two"
}) // v == "TWO", found == true
// FindEx lets the given function decide which value to return
type Person struct {
Name string
Age int
}
// Finds age of person having name "Tim"
ageOfTim, found := FindEx([]Person{{"John", 40}, {"Tim", 50}}, func(p Person) (int, bool) {
return p.Age, p.Name == "Tim"
}) // ageOfTim == 50, found == true
FindLast / FindLastEx
Finds a value in a slice from the end.
v, found := FindLast([]string{"one", "TWO", "ONe"}, func(elem string) bool {
return strings.ToLower(elem) == "one"
}) // v == "ONe", found == true
// FindLastEx lets the given function decide which value to return
type Person struct {
Name string
Age int
}
// Finds age of the last person having name "tim"
ageOfTim, found := FindLastEx([]Person{{"John", 40}, {"Tim", 50}, {"TIM", 60}}, func(p Person) (int, bool) {
return p.Age, strings.ToLower(p.Name) == "tim"
}) // ageOfTim == 60, found == true
IndexOf / IndexOfBy
Finds the index of a value in a slice, returns -1 if not found.
IndexOf([]int{1, 2, 3}, 4) // -1
IndexOf([]int{1, 2, 3}, 2) // 1
// Use IndexOfBy for custom function
IndexOfBy([]string{"one", "TWO"}, func(elem string) bool {
return strings.ToLower(elem) == "two"
}) // 1
LastIndexOf / LastIndexOfBy
Finds the last index of an element in a slice, returns -1 if not found.
LastIndexOf([]int{1, 2, 3}, 4) // -1
LastIndexOf([]int{1, 2, 1, 3}, 1) // 2
CountValue / CountValueBy
Counts the number of occurrences of a value in a slice.
CountValue([]int{1, 2, 3}, 4) // 0
CountValue([]int{1, 2, 3, 2}, 2) // 2
Slice filtering
Filter
Filters a slice with condition.
Filter([]int{1, 2, 3, 4}, func (i int) bool {
return i % 2 == 0
}) // []int{2, 4}
FilterLT([]int{1, 2, 3, 4}, 3) // []int{1, 2}
FilterLTE([]int{1, 2, 3, 4}, 3) // []int{1, 2, 3}
FilterGT([]int{1, 2, 3, 4}, 3) // []int{4}
FilterGTE([]int{1, 2, 3, 4}, 3) // []int{3, 4}
FilterNE([]int{1, 2, 3, 4}, 3) // []int{1, 2, 4}
FilterIN([]int{1, 2, 3, 4}, 3, 2, 7) // []int{2, 3}
FilterNIN([]int{1, 2, 3, 4}, 3, 2, 7) // []int{1, 4}
FilterLIKE([]string{"*Abc*", "*abc*", "abc*", "*abc"}, "Abc") // []string{"*Abc*"}
FilterILIKE([]string{"*Abc*", "*abc*", "abc*", "*abc"}, "Abc") // []string{"*Abc*", "*abc*", "abc*", "*abc"}
Slice iteration
ForEach / ForEachReverse
Iterates over slice content.
ForEach([]int{1, 2, 3}, func (i, v int) {
fmt.Printf("%d ", v)
}) // prints 1 2 3
ForEachReverse([]int{1, 2, 3}, func (i, v int) {
fmt.Printf("%d ", v)
}) // prints 3 2 1
// ForEachPtr can be faster if you iterate over a slice of big structs
ForEachPtr([]BigStruct{...}, func (i, v *BigStruct) { ... })
ForEachPtrReverse([]BigStruct{...}, func (i, v *BigStruct) { ... })
Iter / IterReverse
Iterates over one or multiple slices with ability to stop.
Iter(func (i, v int) bool {
fmt.Printf("%d ", v)
return i < 3
}, []int{1, 2, 3}, []int{4, 5}) // prints 1 2 3 4
IterReverse(func (i, v int) bool {
fmt.Printf("%d ", v)
return true
}, []int{1, 2, 3}, []int{4, 5}) // prints 5 4 3 2 1
// IterPtr can be faster if you iterate over a slice of big structs
IterPtr(func (i, v *BigStruct) bool { ... }, []BigStruct{...})
IterPtrReverse(func (i, v *BigStruct) bool { ... }, []BigStruct{...})
Slice uniqueness
IsUnique / IsUniqueBy
Returns true if a slice contains unique values.
IsUnique([]int{1, 2, 3}) // true
IsUnique([]int{1, 2, 1}) // false
// Use IsUniqueBy for custom function
IsUniqueBy([]string{"one", "ONE"}, strings.ToLower) // false
FindUniques / FindUniquesBy
Finds all unique elements in the given slice. The order of elements in the result is the same as they appear in the input.
FindUniques([]int{1, 2, 3, 2}) // []int{1, 3}
FindUniques([]int{1, 2, 2, 1}) // []int{}
// FindUniquesBy supports custom key function
FindUniquesBy([]string{"one", "ONE", "Two"}, func (s string) string {
return strings.ToLower(s)
}) // []string{"Two"}
FindDuplicates / FindDuplicatesBy
Finds all elements which are duplicated in the given slice. The order of elements in the result is the same as they appear in the input.
FindDuplicates([]int{1, 2, 3, 2}) // []int{2}
FindDuplicates([]int{1, 2, 3}) // []int{}
// FindDuplicatesBy supports custom key function
FindDuplicatesBy([]string{"one", "ONE", "Two"}, func (s string) string {
return strings.ToLower(s)
}) // []string{"one"}
ToSet / ToSetBy / ToSetByReverse
Calculates unique values of a slice.
ToSet([]int{1, 2, 3, 1, 2}) // []int{1, 2, 3}
ToSet([]string{"one", "2", "one"}) // []string{"one", "2"}
// Use ToSetBy for custom key function
ToSetBy([]string{"one", "TWO", "two", "One"}, strings.ToLower) // []string{"one", "TWO"}
// Use ToSetByReverse for iterating items from the end of the list
ToSetByReverse([]string{"one", "TWO", "two", "One"}, strings.ToLower) // []string{"One", "two"}
Slice transformation
MapSlice / MapSliceEx
Transforms a slice to a slice.
MapSlice([]string{"1", "2 ", " 3"}, strings.TrimSpace) // []string{"1", "2", "3"}
// Use MapSliceEx to transform with error handling
MapSliceEx([]string{"1","2","3"}, gofn.ParseInt[int]) // []int{1, 2, 3}
MapSliceEx([]string{"1","x","3"}, gofn.ParseInt[int]) // strconv.ErrSyntax
MapSliceEx([]string{"1","200","3"}, gofn.ParseInt[int8]) // strconv.ErrRange
MapSliceToMap / MapSliceToMapEx
Transforms a slice to a map.
MapSliceToMap([]int{1, 2, 3}, func (i int) (int, string) {
return i, fmt.Sprintf("%d", i)
}) // map[int]string{1: "1", 2: "2", 3: "3"}
// Use MapSliceToMapEx to transform with error handling
MapSliceToMapEx([]string{"1","300","3"}, func (s string) (string, int, bool) {
v, e := gofn.ParseInt[int8](s)
return s, v, e
}) // strconv.ErrRange
MapSliceToMapKeys
Transforms a slice to a map with using slice items as map keys.
MapSliceToMapKeys([]int{1, 2, 3, 2}, "x") // map[int]string{1: "x", 2: "x", 3: "x"}
MapSliceToMapKeys([]int{1, 2, 1}, struct{}{}) // map[int]struct{}{1: struct{}{}, 2: struct{}{}}
Slice algo
Compact
Compacts a slice by removing zero elements. Not change the source slice.
s := Compact([]int{1, 0, 3}) // s == []int{1, 3}
Reverse / ReverseCopy
Reverses slice content.
Reverse([]int{1, 2, 3}) // []int{3, 2, 1}
s1 := []int{1, 2, 3}
s2 := ReverseCopy(s1) // s1 == []int{1, 2, 3}, s2 == []int{3, 2, 1}
Drop
Returns a new slice with dropping values in the specified list.
NOTE: this function is just a call to FilterNIN()
.
Drop([]int{1, 2, 3, 4}, 3, 1) // []int{2, 4}
Shuffle
Shuffle items of a slice. Not change the source slice.
s := Shuffle([]int{1, 2, 3}) // s is a new slice with random items of the input
Chunk / ChunkByPieces
Splits slice content into chunks.
Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}}
ChunkByPieces([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2, 3}, []int{4, 5}}
Union / UnionBy
Finds all unique values from multiple slices.
Union([]int{1, 3, 2}, []int{1, 2, 2, 4}) // []int{1, 3, 2, 4}
Intersection / IntersectionBy
Finds all unique shared values from multiple slices.
Intersection([]int{1, 3, 2}, []int{1, 2, 2, 4}) // []int{1, 2}
Difference / DifferenceBy
Finds all different values from 2 slices.
left, right := Difference([]int{1, 3, 2}, []int{2, 2, 4}) // left == []int{1, 3}, right == []int{4}
Reduce / ReduceEx
Reduces a slice to a value.
Reduce([]int{1, 2, 3}, func (accumulator int, currentValue int) int {
return accumulator + currentValue
}) // 6
ReduceEx([]int{1, 2, 3}, func (accumulator int, currentValue int, i index) int {
return accumulator + currentValue
}, 10) // 16
ReduceReverse / ReduceReverseEx
Reduces a slice to a value with iterating from the end.
ReduceReverse([]int{1, 2, 3}, func (accumulator int, currentValue int) int {
return accumulator + currentValue
}) // 6
ReduceReverseEx([]int{1, 2, 3}, func (accumulator int, currentValue int, i index) int {
return accumulator + currentValue
}, 10) // 16
Partition / PartitionN
Splits a slice into multiple partitions.
// Splits a slice into 2 partitions
p0, p1 := Partition([]int{1, 2, 3, 4, 5}, func (v int, index int) bool {return v%2==0})) // p0 == []int{2, 4}, p1 == []int{1, 3, 5}
// Splits a slice into 3 partitions
p := PartitionN([]int{1, 2, 3, 4, 5}, 3, func (v int, index int) int {return v%3})) // p == [[3], [1, 4], [2, 5]]
Flatten / Flatten3
Flattens multi-dimension slice.
Flatten([]int{1, 2, 3}, []int{4, 5}) // []int{1, 2, 3, 4, 5}
Flatten3([][]int{{1, 2}, {3, 4}, [][]int{{5, 6}, {7}}}) // []int{1, 2, 3, 4, 5, 6, 7}
Zip / Zip<N>
Combines values from multiple slices by each position (N
is from 3 to 5).
Zip([]int{1, 2, 3}, []int{4, 5}) // []*Tuple2{{1, 4), {2, 5}}
Zip3([]int{1, 2, 3}, []string{"4", "5"}, []float32{6.0, 7.0}) // []*Tuple3{{1, "4", 6.0), {2, "5", 7.0}}
Slice conversion
ToIfaceSlice
Converts a slice of any type to a slice of interfaces.
ToIfaceSlice([]int{1, 2, 3}) // []any{1, 2, 3}
ToIfaceSlice([]string{"foo", "bar"}) // []any{"foo", "bar"}
ToStringSlice
Converts a slice of string-approximate type to a slice of strings.
type XType string
ToStringSlice([]XType{XType("foo"), XType("bar")}) // []string{"foo", "bar"}
ToNumberSlice
Converts a slice of number type to a slice of specified number type.
ToNumberSlice[int]([]int8{1, 2, 3}) // []int{1, 2, 3}
ToNumberSlice[float32]([]int{1, 2, 3}) // []float32{1.0, 2.0, 3.0}
type XType int
ToNumberSlice[int]([]XType{XType(1), XType(2)}) // []int{1, 2}
ToSlice
Creates a slice for individual values.
ToSlice(1, 2, 3) // []int{1, 2, 3}
ToPtrSlice
Creates a slice of pointers point to the given elements.
s1 := []int{1, 2, 3}
s2 := ToPtrSlice(s1) // []*int{&s1[0], &s1[1], &s1[2]}
Slice subset
ContainSlice
Returns true if a slice contains another slice.
ContainSlice([]int{1, 2, 3, 4, 5}, []int{2, 3, 4}) // true
ContainSlice([]int{1, 2, 3, 4, 5}, []int{2, 4}) // false
IndexOfSlice
Finds the first occurrence of a sub-slice in a slice.
IndexOfSlice([]int{1, 2, 3, 4, 5}, []int{2, 3, 4}) // 1
IndexOfSlice([]int{1, 2, 3, 4, 5}, []int{2, 4}) // -1
LastIndexOfSlice
Finds the last occurrence of a sub-slice in a slice.
LastIndexOfSlice([]int{1, 2, 3, 1, 2, 3, 4}, []int{1, 2, 3}) // 3
LastIndexOfSlice([]int{1, 2, 3, 4, 5}, []int{2, 4}) // -1
SubSlice
Returns sub slice of a slice in range [start, end). end
param is exclusive. This function doesn't raise error.
Passing negative numbers for start
and end
to get items from the end of the slice.
SubSlice([]int{1, 2, 3}, 0, 2) // []{1, 2}
SubSlice([]int{1, 2, 3}, -1, -2) // []{3}
SubSlice([]int{1, 2, 3}, -1, -3) // []{2, 3}
Map
MapEqual / MapEqualBy
Returns true if 2 maps equal.
MapEqual(map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}) // true
MapEqual(map[int]string{1: "one", 2: "two"}, map[int]string{1: "one", 2: "TWO"}) // false
MapContainKeys
Returns true if a map contains all given keys.
MapContainKeys(map[int]int{1: 11, 2: 22}, 1) // true
MapContainKeys(map[int]int{1: 11, 2: 22}, 1, 2) // true
MapContainKeys(map[int]int{1: 11, 2: 22}, 1, 3) // false
MapContainValues
Returns true if a map contains all given values.
MapContainValues(map[int]int{1: 11, 2: 22}, 11) // true
MapContainValues(map[int]int{1: 11, 2: 22}, 11, 22) // true
MapContainValues(map[int]int{1: 11, 2: 22}, 11, 33) // false
MapIter
Iterates over the entries of one or multiple maps.
MapIter(func(k int, v string) { ... }, map[int]string{1: "11", 2: "22"}, map[int]string{3: "33", 4: "44"})
MapKeys
Gets all keys of a map.
MapKeys(map[int]int{1: 11, 2: 22}) // []int{1, 2} (note: values may be in different order)
MapValues
Gets all values of a map.
MapValues(map[int]int{1: 11, 2: 22, 3: 22}) // []int{11, 22, 22} (note: values may be in different order)
MapEntries
Gets all entries (key, value) of a map.
MapEntries(map[int]int{1: 11, 2: 22}) // []*Tuple2[int,int]{{1,11}, {2,22}} (note: values may be in different order)
MapGet
Retrieves map value for a key, returns the default value if not exist.
MapGet(map[int]int{1: 11, 2: 22}, 1, 0) // 11 (found)
MapGet(map[int]int{1: 11, 2: 22}, 3, 0) // 0 (not found)
MapPop
Removes entry from a map and returns the current value if found.
MapPop(map[int]int{1: 11, 2: 22}, 1, 0) // 11 (found)
MapPop(map[int]int{1: 11, 2: 22}, 3, 0) // 0 (not found)
MapSetDefault
Sets default value for a key and returns the current value.
MapSetDefault(map[int]int{1: 11, 2: 22}, 1, 0) // 11 (no value added to the map)
MapSetDefault(map[int]int{1: 11, 2: 22}, 3, 0) // 0 (entry [3, 0] is added to the map)
MapUpdate / MapUpdateExistingOnly / MapUpdateNewOnly
Updates a map content with another map.
s := map[int]int{1: 11, 2: 22}
MapUpdate(s, map[int]int{1: 111, 3: 33}) // s == map[int]int{1: 111, 2: 22, 3: 33}
MapUpdateExistingOnly(s, map[int]int{2: 222, 3: 33}) // s == map[int]int{1: 11, 2: 222}
MapUpdateNewOnly(s, map[int]int{2: 222, 3: 33}) // s == map[int]int{1: 11, 2: 22, 3: 33}
MapCopy
Copies a map.
MapCopy(map[int]int{1: 11, 2: 22, 3: 33}) // map[int]int{1: 11, 2: 22, 3: 33}
MapPick
Copies map content for specified keys only.
MapPick(map[int]int{1: 11, 2: 22, 3: 33}, 2, 3, 2) // map[int]int{2: 22, 3: 33}
MapOmit
Omits keys from a map.
m := map[int]int{1: 11, 2: 22, 3: 33}
MapOmit(m, 2, 3, 4) // m == map[int]int{1: 11}
MapOmitCopy
Copies map content with omitting specified keys.
MapOmitCopy(map[int]int{1: 11, 2: 22, 3: 33}, 2, 3, 4) // map[int]int{1: 11}
MapReverse
Returns a new map with exchanging keys and values of the given map.
m, dupKeys := MapReverse(map[int]int{1: 11, 2: 22, 3: 33}) // m == map[int]int{11: 1, 22: 2, 33: 3} and dupKeys == []int{}
m, dupKeys := MapReverse(map[int]int{1: 11, 2: 11, 3: 33}) // m == map[int]int{11: <1 or 2>, 33: 3} and dupKeys == []int{1, 2}
Struct
StructToMap
Converts struct contents to a map. This function is a shortcut to rflutil.StructToMap.
ParseTag / ParseTagOf / ParseTagsOf
Parses struct tags. These functions are shortcuts to rflutil.ParseTag.
String
RuneLength
Alias of utf8.RuneCountInString
.
len("café") // 5
RuneLength("café") // 4
RandString / RandStringEx
Generates a random string.
RandString(10) // Generates a string of 10 characters from alphabets and digits
RandStringEx(10, []rune("0123456789")) // Generates a string of 10 characters from the specified ones
StringJoin / StringJoinEx / StringJoinBy
Joins a slice of any element type.
s := StringJoin([]int{1,2,3}, ", ") // s == "1, 2, 3"
type Struct struct {
I int
S string
}
s := StringJoinBy([]Struct{{I:1, s:"a"}, {I:2, s:"b"}}, ", ", func (v Struct) string {
return fmt.Sprintf("%d:%s", v.I, v.S)
}) // s == "1:a, 2:b"
StringLexJoin / StringLexJoinEx
Joins a slice of any element type in lexical manner.
StringLexJoin([]int{1,2,3}, ", ", " and ") // return "1, 2 and 3"
// Use a custom format string
StringLexJoinEx([]int64{254, 255}, ", ", " or ", "%#x") // returns "0xfe or 0xff"
StringWrap / StringUnwrap
Wraps/Unwraps a string with the given tokens.
StringWrap("abc", "*") // "*abc*"
StringWrapLR("abc", "[", "]") // "[abc]"
StringUnwrap("*abc*", "*") // "abc"
StringUnwrapLR("[abc]", "[", "]") // "abc"
Number
ParseInt / ParseUint / ParseFloat
Parses a number using strconv.ParseInt then converts the value to a specific type.
ParseInt[int16]("111") // int16(111)
ParseInt[int8]("128") // strconv.ErrRange
// Return default value on failure
ParseIntDef("200", 10) // int(200)
ParseIntDef("200", int8(10)) // int8(10)
// Parse integer with specific base
ParseIntEx[int8]("eeff1234", 16) // strconv.ErrRange
ParseIntEx[int]("eeff1234", 16) // int value for "eeff1234"
// Parse string containing commas
ParseInt[int]("1,234,567") // strconv.ErrSyntax
ParseIntUngroup[int]("1,234,567") // int(1234567)
- NOTE: There are also ParseUint for unsigned integers and ParseFloat for floating numbers.
FormatInt / FormatUint / FormatFloat
Formats a number value.
FormatInt(123) // "123"
// Format number with specific format string (use fmt.Sprintf)
FormatIntEx(123, "%05d") // "00123"
// Format number with decimal grouping
FormatIntGroup(1234567) // "1,234,567"
- NOTE: There are also FormatUint for unsigned integers and FormatFloat for floating numbers.
NumberFmtGroup
Groups digits of a number. Input number is of type string.
NumberFmtGroup("1234567", '.', ',') // "1,234,567"
NumberFmtGroup("1234567", ',', '.') // "1.234.567"
NumberFmtGroup("1234567.12345", '.', ',') // "1,234,567.12345"
ToSafeInt<N> / ToSafeUint<N>
Safely casts an integer of any type to a specific type.
v, err := ToSafeInt8(-129) // err is ErrOverflow
v, err := ToSafeInt16(math.MaxUint16) // err is ErrOverflow
v, err := ToSafeUint32(math.MaxUint64) // err is ErrOverflow
v, err := ToSafeUint64(-1) // err is ErrOverflow
v, err := ToSafeInt8(127) // err == nil, v == int8(127)
v, err := ToSafeUint16(math.MaxInt16) // err == nil, v == uint16(math.MaxInt16)
v, err := ToSafeInt32(math.MaxInt32) // err == nil, v == int32(math.MaxInt32)
v, err := ToSafeUint64(uint64(math.MaxUint64)) // err == nil, v == uint64(math.MaxUint64)
Concurrency
ExecTasks / ExecTasksEx
Executes tasks concurrently with ease. This function provides a convenient way for one of the most popular use case in practical.
// In case you want to store the task results into a shared variable,
// make sure you use enough synchronization
var task1Result any
var task2Result []any
// Allow spending maximum 10s to finish all the tasks
ctx := context.WithTimeout(context.Background(), 10 * time.Second)
err := ExecTasks(ctx, 0 /* max concurrent tasks */,
// Task 1st:
func(ctx context.Context) (err error) {
task1Result, err = getDataFromDB()
return err
},
// Task 2nd:
func(ctx context.Context) (err error) {
for i:=0; i<10; i++ {
if err := ctx.Err(); err != nil {
return err
}
task2Result = append(task2Result, <some data>)
return nil
}
},
)
if err != nil {
// one or more tasks failed
}
ExecTaskFunc / ExecTaskFuncEx
Executes a task function on every target objects concurrently. This function is similar to ExecTasks()
, but it
takes only one function and a list of target objects.
var mu sync.Mutex
var evens []int
var odds []any
taskFunc := func(ctx context.Context, v int) error {
mu.Lock()
if v%2 == 0 {
evens = append(evens, v)
} else {
odds = append(odds, v)
}
mu.Unlock()
return nil
}
err := ExecTaskFunc(ctx, 0 /* max concurrent tasks */, taskFunc, 1, 2, 3, 4, 5)
if err != nil {
// one or more tasks failed
}
// Result is: evens has [2, 4], odds has [1, 3, 5] (with undetermined order of items)
Function
Bind<N>Arg<M>Ret
Fully binds a function with returning a function which requires no argument.
func myCalc(a1 int, a2 string) error { ... }
myQuickCalc := Bind2Arg1Ret(myCalc, 100, "hello")
err := myQuickCalc()
Partial<N>Arg<M>Ret
Partially binds the first argument of a function.
func myCalc(a1 int, a2 string) error { ... }
myQuickCalc := Partial2Arg1Ret(myCalc, 100)
err := myQuickCalc("hello")
Randomization
NOTE: Should not use these functions for crypto purpose.
RandChoice
Picks up an item randomly from a list of items.
val, valid := RandChoice(1, 2, 3) // valid == true and `val` is one of the input items
val, valid := RandChoice[int]() // valid == false and `val` is int(0)
RandChoiceMaker
Provides a method to pick up items randomly from a list of items without duplication of choice.
choiceMaker := NewRandChoiceMaker([]int{1, 2, 3})
for choiceMaker.HasNext() {
randItem, _ := choiceMaker.Next()
}
// OR
for {
randItem, valid := choiceMaker.Next()
if !valid {
break
}
}
Error handling
ErrWrap / ErrWrapL
Convenient functions to wrap a single error with a single message.
// shortcut call of fmt.Errorf("%w: %s")
e := ErrWrap(err, "failed to do something")
// shortcut call of fmt.Errorf("%s: %w")
e := ErrWrapL("failed to do something", err)
ErrUnWrap
Unwraps an error into a slice of errors. This function can unwrap an error created by errors.Join()
and
fmt.Errorf(<one or multiple errors passed here>)
.
e1 := errors.New("e1")
e2 := errors.New("e2")
e3 := errors.Join(e1, e2)
e4 := fmt.Errorf("%w, %w", e1, e2)
errs := ErrUnwrap(e1) // errs == []error{e1}
errs := ErrUnwrap(e3) // errs == []error{e1,e2}
errs := ErrUnwrap(e4) // errs == []error{e1,e2}
ErrUnwrapToRoot
Unwraps an error until the deepest one.
e1 := errors.New("e1")
e2 := fmt.Errorf("e2: %w", e1)
e3 := fmt.Errorf("e3: %w", e2)
e := ErrUnwrapToRoot(e3) // e == e1
e := ErrUnwrapToRoot(e2) // e == e1
Time
MinTime / MaxTime / MinMaxTime
Finds minimum/maximum time value in a slice.
t0 := time.Time{}
t1 := time.Date(2000, time.December, 1, 0, 0, 0, 0, time.UTC)
t2 := time.Date(2001, time.December, 1, 0, 0, 0, 0, time.UTC)
MinTime(t0, t1, t2) // t0
MinTime(t1, t2) // t1
MaxTime(t0, t1, t2) // t2
MinMaxTime(t0, t1, t2) // t0, t2
ExecDuration / ExecDurationN
Measures time executing a function.
duration := ExecDuration(func() { // do something })
// The given function returns a value
outVal, duration := ExecDuration1(func() string { return "hello" }) // outVal == "hello"
// The given function returns 2 values
outVal1, err, duration := ExecDuration2(func() (int, error) { return 123, nil }) // outVal1 == 123, err == nil
ExecDelay
Executes a function after a time duration. This function is just an alias of time.AfterFunc
.
timer := ExecDelay(3*time.Second, func() {
// do something after waiting 3 seconds
})
Math
All
Returns true
if all given values are evaluated true
.
All(1, "1", 0.5) // true
All(1, "1", 0.0) // false
All(1, "", -1) // false
All() // true
Any
Returns true
if any of the given values is evaluated true
.
Any(1, "", 0.5) // true
Any(1, "1", 0.0) // true
Any(0, "", 0.0) // false
Any() // false
Abs
Calculates absolute value of an integer.
Abs(-123) // int64(123)
Abs(123) // int64(123)
Abs(math.MinInt64) // math.MinInt64 (special case)
Clamp
Clamps a value within a range (lower and upper bounds are inclusive).
Clamp(3, 10, 20) // 10
Clamp(30, 10, 20) // 20
Clamp(15, 10, 20) // 15
Min / Max / MinMax
Finds minimum/maximum value in a slice.
Min(1, 2, 3, -1) // -1
MinIn([]int{1, 2, 3, -1}) // -1
MinInBy([]string{"a", "B"}, func(a, b int) bool {
return strings.ToLower(a) < strings.ToLower(b)
}) // "a"
Max(1, 2, 3, -1) // 3
MaxIn([]int{1, 2, 3, -1}) // 3
MaxInBy([]string{"a", "B"}, func(a, b int) bool {
return strings.ToLower(a) < strings.ToLower(b)
}) // "B"
MinMax(1, 2, 3, -1) // -1, 3
Sum / SumAs
Calculates sum
value of slice elements.
Sum([]int{1, 2, 3}) // 6
SumAs[int]([]int8{50, 60, 70}) // 180 (Sum() will fail as the result overflows int8)
Product / ProductAs
Calculates product
value of slice elements.
Product([]int{1, 2, 3}) // 6
ProductAs[int]([]int8{5, 6, 7}) // 210 (Product() will fail as the result overflows int8)
Utility
FirstNonEmpty
Returns the first non-empty value in the given arguments if found, otherwise returns the zero value.
This function uses reflection
. You can connsider using Coalesce
for generic types.
Values considered "non-empty" are:
- not empty values (0, empty string, false, nil, ...)
- not empty containers (slice, array, map, channel)
- not pointers that point to zero/empty values
FirstNonEmpty(0, -1, 2, 3) // -1
FirstNonEmpty("", " ", "b") // " "
FirstNonEmpty([]int{}, nil, []int{1}, []int{2, 3}) // []int{1}
FirstNonEmpty([]int{}, nil, &[]int{}, []int{2, 3}) // []int{2, 3}
FirstNonEmpty[any](nil, 0, 0.0, "", struct{}{}) // nil (the first zero value)
Coalesce
Returns the first non-zero argument. Input type must be comparable.
Coalesce(0, -1, 2) // -1
Coalesce("", " ", "s") // " "
Coalesce[*int](ptr1, ptr2) // the first non-nil pointer
If
A convenient function works like C ternary operator.
NOTE: This function is deprecated as it may cause the program to crash on misuses due to both passing
expressions are evaluated regardless of the condition.
For example: firstItem := If(len(slice) > 0, slice[0], defaultVal)
will crash if slice
is empty as
the expression slice[0]
is always evaluated. Use it at your own risk.
val := If(x > 100, val1, val2) // If x > 100, val == val1, otherwise val == val2
Must<N>
Must<N> ( N is from 1 to 6) functions accept a number of arguments with the last one is of error
type.
Must<N> functions return the first N-1 arguments if the error is nil
, otherwise they panic.
func CalculateAmount() (int, error) {}
amount := Must(CalculateAmount()) // panic on error, otherwise returns the amount
func CalculateData() (int, string, float64, error) {}
v1, v2, v3 := Must4(CalculateData()) // panic on error, otherwise returns the 3 first values
ToPtr
Returns a pointer to the input argument.
func aFunc(ptr *int) {}
// Use ToPtr to pass a value inline
aFunc(ToPtr(10))
Head
Takes the first argument.
Head(1, "2", 1.0, 3) // 1
Tail
Takes the last argument.
Tail[string](true, "2", 1.0, "3") // "3"
Benchmarks
Equal vs ContentEqual vs reflect.DeepEqual
Benchmark_Slice_Equal/StructSlice/Equal
Benchmark_Slice_Equal/StructSlice/Equal-8 510845715 2.047 ns/op
Benchmark_Slice_Equal/StructSlice/ContentEqual
Benchmark_Slice_Equal/StructSlice/ContentEqual-8 583167950 2.061 ns/op
Benchmark_Slice_Equal/StructSlice/DeepEqual
Benchmark_Slice_Equal/StructSlice/DeepEqual-8 15403771 79.19 ns/op
Benchmark_Slice_Equal/IntSlice/Equal
Benchmark_Slice_Equal/IntSlice/Equal-8 589706185 2.087 ns/op
Benchmark_Slice_Equal/IntSlice/ContentEqual
Benchmark_Slice_Equal/IntSlice/ContentEqual-8 523120755 2.194 ns/op
Benchmark_Slice_Equal/IntSlice/DeepEqual
Benchmark_Slice_Equal/IntSlice/DeepEqual-8 15243183 77.93 ns/op
Contributing
- You are welcome to make pull requests for new functions and bug fixes.