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:

Contents

Installation

go get github.com/tiendc/gofn

Usage

Functions for slices


Equal / EqualBy

Returns true if 2 slices have the same size and all elements of them equal in the current order. This function is equivalent to reflect.DeepEqual(), but faster (for how much faster, see Benchmark section).

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

Concat

Concatenates two or more slices.

Concat([]int{1}, []int{2}, []int{2, 3}) // []int{1, 2, 2, 3}

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

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

Find

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

FindLast

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

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

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

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)

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}

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}

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}}

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}

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

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

GetFirst

Returns the first element of slice if it is not empty, otherwise return the default value.

GetFirst([]int{1, 2, 3}, 4) // 1
GetFirst([]int{}, 11)       // 11

GetLast

Returns the last element of slice if it is not empty, otherwise return the default value.

GetLast([]int{1, 2, 3}, 4) // 3
GetLast([]int{}, 11)       // 11

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}

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}

Functions for maps


MapEqual / MapEqualBy

Returns true if 2 maps equal. This function is equivalent to reflect.DeepEqual(), but faster (for how much faster, see Benchmark section).

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

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)

MapUpdate / MapUpdateXX

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}

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)

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}

Functions for structs


StructToMap / StructToMapEx

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.

Functions for strings


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 / 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"

MultilineString

Removes all leading spaces from every line in the given string. This function is useful to declare a string with a neat multiline-style.

func DoSomething() {
    // Commonly you may use this style to create multiline string in Go (which looks ugly)
    s := `
line-1 abc xyz
line-2 abc xyz
`
    // Use this function
    s := MultilineString(
        `line-1 abc xyz
        line-2 abc xyz`
    )
}

LinesTrim/LinesTrimSpace

Removes all certain leading and trailing characters from every line in the given string.

LinesTrimSpace("  line-1  \n  line-2  ")      // "line-1\nline-2"
LinesTrim("a line-1 b \n a line-2 ab", " ba") // "line-1\nline2"

LinesTrimLeft/LinesTrimLeftSpace

Removes all certain leading characters from every line in the given string.

LinesTrimLeftSpace("  line-1  \n  line-2  ")      // "line-1  \nline-2  "
LinesTrimLeft("ab line-1  \n a line-2 ab", " ba") // "line-1  \nline2 ab"

LinesTrimRight/LinesTrimRightSpace

Removes all certain trailing characters from every line in the given string.

LinesTrimRightSpace("  line-1  \n  line-2  ")    // "  line-1\nline-2"
LinesTrimRight("line-1 b \n a line-2 ab", " ba") // "line-1\n a line2"

Functions for numbers


ParseInt / ParseIntXX

Parses integer 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 / FormatIntXX

Formats an integer.

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.

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

Functions for 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)

Iteration functions


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

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

Transformation functions


Filter / FilterXX

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"}

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"}

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{}{}}

Reverse

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}

Flatten

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

Combines values from multiple slices by each position.

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}}

Conversion functions


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}

Bind functions


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()

Randomization functions

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
    }
}

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

RandString

Generates a random string.

RandString(10)                     // a random string has 10 characters (default of alphabets and digits)
RandStringEx(10, []rune("01234"))  // a random string has 10 characters (only 0-4)

Sorting functions


Sort / SortDesc

Convenient wrapper 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

Math functions


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}

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)

Reduce / ReduceEx

Reduces a slice to a value.

Reduce([]int{1, 2, 3}, func (accumulator int, currentValue int) int {
    return accumulator + currentValue
}) // 6

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]]

Min / MinIn / MinInBy / Max / MaxIn / MaxInBy / MinMax

Finds minimum/maximum value in a slice.

Min(1, 2, 3, -1)    // -1
Max(1, 2, 3, -1)    // 3
MinMax(1, 2, 3, -1) // -1, 3

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

Time functions


MinTime / MaxTime

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(2000, time.December, 2, 0, 0, 0, 0, time.UTC)
MinTime(t0, t1, t2) // t0
MinTime(t1, t2)     // t1
MaxTime(t0, t1, t2) // t2

ExecDuration / ExecDurationN (N from 1 to 3)

Measures time executing a function.

duration := ExecDuration(func() { // do somthing })
// 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

Common functions


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

MustN (N is from 1 to 6)

MustN functions accept a number of arguments with the last one is of error type. MustN 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

If

A convenient function works like C ternary operator.

NOTE: This function is deprecated as it has side effect of both expressions are evaluated and may cause the program to crash. For example: firstItem := If(len(slice) > 0, slice[0], defaultVal) will crash if slice is empty as the expression slice[0] is evaluated before the function call. Use it at your own risk.

val := If(x > 100, val1, val2) // If x > 100, val == val1, otherwise val == val2

Or

Logically selects the first value which is not zero value of type T. This function is similar to FirstTrue, but it uses generic, not reflection, and it accepts primitive types only.

Or(0, -1, 2)         // -1
Or("", " ", "s")     // " "
Or[*int](ptr1, ptr2) // the first non-nil pointer

New

Creates a new variable and return the address of it. Very helpful in unit testing.

func f(ptr *int) {}

// Normally we need to declare a var before accessing its address
val := 10
f(&val)

// With using New
f(New(10))

Head / HeadOf

Takes the first argument.

Head(1, "2", 1.0, 3)   // 1

HeadOf([]int{1, 2, 3}) // 1, true
HeadOf([]string{})     // "", false

Tail / TailOf

Takes the last argument.

Tail[string](true, "2", 1.0, "3") // "3"

TailOf([]int{1, 2, 3}) // 3, true
TailOf([]string{})     // "", false

FirstTrue

Returns the first "true" value in the given arguments if found. Values considered "true" are:

  • not zero values (0, empty string, false, nil, ...)
  • not empty containers (slice, array, map, channel)
  • not pointers that point to zero/empty values
FirstTrue(0, 0, -1, 2, 3)                       // -1
FirstTrue("", "", " ", "b")                     // " "
FirstTrue([]int{}, nil, []int{1}, []int{2, 3})  // []int{1}
FirstTrue([]int{}, nil, &[]int{}, []int{2, 3})  // []int{2, 3}
FirstTrue[any](nil, 0, 0.0, "", struct{}{})     // nil (the first zero value)

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