Skip to content
/ gofn Public

High performance utility functions using Generics

License

Notifications You must be signed in to change notification settings

tiendc/gofn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Version GoDoc Build Status Coverage Status GoReport

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:

Installation

go get github.com/tiendc/gofn

Function list

Slice

Slice searching

Slice filtering

Slice iteration

Slice uniqueness

Slice transformation

Slice algo

Slice conversion

Slice subset

Map

Struct

String

Number

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.

License