gofn
High performance utility functions written using Generics for Go 1.18+
About gofn
This large README is shown as a plain-text preview. Read the full README at the source
[![Go Version][gover-img]][gover] [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![GoReport][rpt-img]][rpt]
# gofn - Utility functions for Go
This is a collection of generics utility functions for Go 1.20+.
## Functionalities
`gofn` consists of useful and convenient functions for most common needs when working on slices, maps, structs, transformation, conversion, and so on.
Check out my other libs:
- [go-deepcopy](https://github.com/tiendc/go-deepcopy): True deep copy function
- [go-validator](https://github.com/tiendc/go-validator): Validation library
- [go-apperrors](https://github.com/tiendc/go-apperrors): Manipulating application errors
- [go-csvlib](https://github.com/tiendc/go-csvlib): High-level rich functionality CSV encoding and decoding library
## Installation
```shell
go get github.com/tiendc/gofn
```
## Function list
**Slice**
- [Equal / EqualBy](#equal--equalby)
- [ContentEqual / ContentEqualBy](#contentequal--contentequalby)
- [Sort / IsSorted](#sort--issorted)
- [RemoveAt](#removeat)
- [FastRemoveAt](#fastremoveat)
- [Remove](#remove)
- [FastRemove](#fastremove)
- [RemoveLastOf](#removelastof)
- [FastRemoveLastOf](#fastremovelastof)
- [RemoveAll](#removeall)
- [Replace / ReplaceN / ReplaceAll](#replace--replacen--replaceall)
- [Splice / SpliceEx](#splice--spliceex)
- [Concat](#concat)
- [Fill](#fill)
- [SliceByRange](#slicebyrange)
- [First / FirstOr](#first--firstor)
- [Last / LastOr](#last--lastor)
**Slice searching**
- [Contain](#contain--containby)
- [ContainAll](#containall)
- [ContainAny](#containany)
- [Find / FindEx](#find--findex)
- [FindLast / FindLastEx](#findlast--findlastex)
- [IndexOf / IndexOfBy](#indexof--indexofby)
- [LastIndexOf / LastIndexOfBy](#lastindexof--lastindexofby)
- [CountValue / CountValueBy](#countvalue--countvalueby)
**Slice filtering**
- [Filter](#filter)
- [FilterNE](#filter)
- [FilterLT / FilterLTE](#filter)
- [FilterGT / FilterGTE](#filter)
- [FilterRange](#filter)
- [FilterIN / FilterNIN](#filter)
- [FilterLIKE / FilterILIKE](#filter)
**Slice iteration**
- [ForEach / ForEachReverse](#foreach--foreachreverse)
- [Iter / IterReverse](#iter--iterreverse)
**Slice uniqueness**
- [IsUnique / IsUniqueBy](#isunique--isuniqueby)
- [FindUniques / FindUniquesBy](#finduniques--finduniquesby)
- [FindDuplicates / FindDuplicatesBy](#findduplicates--findduplicatesby)
- [ToSet / ToSetBy / ToSetByReverse ](#toset--tosetby--tosetbyreverse)
**Slice transformation**
- [MapSlice / MapSliceEx](#mapslice--mapsliceex)
- [MapSliceToMap / MapSliceToMapEx](#mapslicetomap--mapslicetomapex)
- [MapSliceToMapKeys](#mapslicetomapkeys)
**Slice algo**
- [Compact](#compact)
- [Drop](#drop)
- [Reverse / ReverseCopy](#reverse--reversecopy)
- [Shuffle](#shuffle)
- [Chunk / ChunkByPieces](#chunk--chunkbypieces)
- [Union / UnionBy](#union--unionby)
- [Intersection / IntersectionBy](#intersection--intersectionby)
- [Difference / DifferenceBy](#difference--differenceby)
- [Reduce / ReduceEx](#reduce--reduceex)
- [ReduceReverse / ReduceReverseEx](#reducereverse--reducereverseex)
- [Partition / PartitionN](#partition--partitionn)
- [Flatten / Flatten3](#flatten--flatten3)
- [Zip / Zip\<N\>](#zip--zipn)
**Slice conversion**
- [ToIfaceSlice](#toifaceslice)
- [ToStringSlice](#tostringslice)
- [ToNumberSlice](#tonumberslice)
- [ToSlice](#toslice)
- [ToSliceSkippingNil / ToSliceSkippingZero](#tosliceskippingnil--tosliceskippingzero)
- [ToPtrSlice](#toptrslice)
**Slice subset**
- [ContainSlice](#containslice)
- [IndexOfSlice](#indexofslice)
- [LastIndexOfSlice](#lastindexofslice)
- [SubSlice](#subslice)
**Map**
- [MapEqual / MapEqualBy](#mapequal--mapequalby)
- [MapContainKeys](#mapcontainkeys)
- [MapContainValues](#mapcontainvalues)
- [MapIter](#mapiter)
- [MapKeys](#mapkeys)
- [MapValues](#mapvalues)
- [MapEntries](#mapentries)
- [MapGet](#mapget)
- [MapPop](#mappop)
- [MapSetDefault](#mapsetdefault)
- [MapUpdate / MapUpdateExistingOnly / MapUpdateNewOnly](#mapupdate--mapupdateexistingonly--mapupdatenewonly)
- [MapCopy](#mapcopy)
- [MapPick](#mappick)
- [MapOmit](#mapomit)
- [MapOmitCopy](#mapomitcopy)
- [MapReverse](#mapreverse)
**Struct**
- [StructToMap](#structtomap)
- [ParseTag / ParseTagOf / ParseTagsOf](#parsetag--parsetagof--parsetagsof)
**String**
- [RuneLength](#runelength)
- [RandString / RandStringEx](#randstring--randstringex)
- [StringSplit / StringSplitN](#stringsplit--stringsplitn)
- [StringJoin / StringJoinEx / StringJoinBy](#stringjoin--stringjoinex--stringjoinby)
- [StringLexJoin / StringLexJoinEx](#stringlexjoin--stringlexjoinex)
- [StringWrap / StringUnwrap](#stringwrap--stringunwrap)
- [StringToUpper1stLetter / StringToLower1stLetter](#stringtoupper1stletter--stringtolower1stletter)
**Number**
- [ParseInt / ParseUint / ParseFloat](#parseint--parseuint--parsefloat)
- [FormatInt / FormatUint / FormatFloat](#formatint--formatuint--formatfloat)
- [NumberFmtGroup](#numberfmtgroup)
- [ToSafeInt\<N\> / ToSafeUint\<N\>](#tosafeintn--tosafeuintn)
**Time**
- [MinTime / MaxTime / MinMaxTime](#mintime--maxtime--minmaxtime)
- [ExecDuration / ExecDurationN](#execduration--execdurationn)
- [ExecDelay](#execdelay)
**Math**
- [All](#all)
- [Any](#any)
- [Abs](#abs)
- [Clamp](#clamp)
- [Min / Max / MinMax](#min--max--minmax)
- [Sum / SumAs](#sum--sumas)
- [Product / ProductAs](#product--productas)
**Concurrency**
- [ExecTasks / ExecTasksEx](#exectasks--exectasksex)
- [ExecTaskFunc / ExecTaskFuncEx](#exectaskfunc--exectaskfuncex)
**Function**
- [Bind\<N\>Arg\<M\>Ret ](#bindnargmret)
- [Partial\<N\>Arg\<M\>Ret ](#partialnargmret)
**Execute Retry**
- [ExecRetry / ExecRetryN](#execretry--execretryn)
- [ExecRetryCtx / ExecRetryCtxN](#execretryctx--execretryctxn)
**Randomization**
- [RandChoice](#randchoice)
- [RandChoiceMaker](#randchoicemaker)
- [RandToken / RandTokenAsHex](#randtoken--randtokenashex)
**Error handling**
- [ErrWrap / ErrWrapL](#errwrap--errwrapl)
- [ErrUnwrap](#errunwrap)
- [ErrUnwrapToRoot](#errunwraptoroot)
**Utility**
- [FirstNonEmpty](#firstnonempty)
- [Coalesce](#coalesce)
- [If](#if)
- [Must\<N\>](#mustn)
- [ToPtr](#toptr)
- [PtrValueOrEmpty](#ptrvalueorempty)
- [Head](#head)
- [Tail](#tail)
### Slice
---
#### Equal / EqualBy
Returns true if 2 slices have the same size and all elements of them equal in the current order.
```go
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.
```go
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.
```go
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.
```go
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`.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
s := []int{1, 2, 1, 3, 1}
RemoveAll(&s, 1) // s == []int{2, 3}
```
#### Replace / ReplaceN / ReplaceAll
Replaces a value with another value.
```go
// 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.
```go
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.
```go
Concat([]int{1}, []int{2}, []int{2, 3}) // []int{1, 2, 2, 3}
```
#### Fill
Fills a slice with specified value.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
CountValue([]int{1, 2, 3}, 4) // 0
CountValue([]int{1, 2, 3, 2}, 2) // 2
```
### Slice filtering
---
#### Filter
Filters a slice with condition.
```go
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}
FilterRange([]int{1, 2, -2, 4}, 0, 3) // []int{1, 2}
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
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.
```go
s := Compact([]int{1, 0, 3}) // s == []int{1, 3}
```
#### Reverse / ReverseCopy
Reverses slice content.
```go
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()`.
```go
Drop([]int{1, 2, 3, 4}, 3, 1) // []int{2, 4}
```
#### Shuffle
Shuffle items of a slice. Not change the source slice.
```go
s := Shuffle([]int{1, 2, 3}) // s is a new slice with random items of the input
```
#### Chunk / ChunkByPieces
Splits slice content into chunks.
```go
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.
```go
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.
```go
Intersection([]int{1, 3, 2}, []int{1, 2, 2, 4}) // []int{1, 2}
```
Frequently Asked Questions
What is gofn?
gofn is a Utilities library for the Go programming language. High performance utility functions written using Generics for Go 1.18+
How do I install gofn?
Install gofn with the Go module system using `go get tiendc/gofn`. Check the repository for the current installation instructions.
What category does gofn belong to?
gofn is listed under Utilities, specifically Utilities.