[GO] Some Slice helper functions using Generic
When developing application with Golang, i sometime have to write helper functions for that working with slice. There are common functions that i use frequence as:
- Check if slice contain an element
- Filter out slice element that match some condition
- Map a slice to a diffrence type
It was a common case that i have to rewrite every helper for difference type of data, and it’s so annoy.
With the recent version 18 of Go, i decided to rewrite the common slice function with the help of Generic. As an expected result, generic work good and help me avoid the redundant/dry code.
Here are some slice helper functions that i want to share:
Contains: Check if a slice contain an element
func Contains[T comparable](slice []T, element T) bool {
for _, e := range slice {
if e == element {
return true
}
}
return false
}
Usage
var stringSlice = []string{"item 1", "item 2"}
var intSlice = []int{1, 2, 3}
fmt.Println(Contains(stringSlice, "item 2")) // true
fmt.Println(Contains(intSlice, 4)) // false
Filter: Filter a slice to return only the elements that match a condition
func Filter[T comparable](slice []T, predicate func(T) bool) []T {
var result []T
for _, e := range slice {
if predicate(e) {
result = append(result, e)
}
}
return result
}
Usage
var intSlice = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
var oddSlice = Filter(intSlice, func(element int) bool {
return element % 2 != 0
})
fmt.Println(oddSlice) // []int{1, 3, 5, 7, 9}
Map: Transform a slice into a diffrence slice type
func Map[T comparable, R comparable](slice []T, mapper func(T) R) []R {
var result []R
for _, e := range slice {
result = append(result, mapper(e))
}
return result
}
Usage
var intSlice = []int{1, 2, 3}
var strSlice = Map(intSlice, func(element int) string {
return strconv.Itoa(element)
})
fmt.Println(strSlice) // []string{"1", "2", "3"}
Repeat: Create a slice with a repeated values
func Repeat[T comparable](input T, time int) []T {
var result []T
var i = 0
for i < time {
result = append(result, input)
i++
}
return result
}
Usage
var intSlice = Repeat(1, 3)
fmt.Println(intSlice) // []int{1, 1, 1}
Overlap: Get the overlap elements beetween two slice
func Overlap[T comparable](slice1 []T, slice2 []T) []T {
result := make([]T, 0)
for _, e1 := range slice1 {
if SliceContains(slice2, e1) {
result = append(result, e1)
}
}
return result
}
Usage
var slice1 = []int{1, 2, 3}
var slice2 = []int{2, 3, 4}
var result = Overlap(slice1, slice2)
fmt.Println(result) // []int{2, 3}
AppendIfNotExists: Append an element to a slice only if it isn’t existed
func AppendIfNotExists[T comparable](slice []T, newItem T) []T {
for _, s := range slice {
if s == newItem {
return slice
}
}
return append(slice, newItem)
}
Usage
var intSlice = []int{1, 2, 3}
var intSlice1 = AppendIfNotExists(intSlice, 3)
var intSlice2 = AppendIfNotExists(intSlice, 4)
fmt.Println(intSlice1) // []int{1, 2, 3}
fmt.Println(intSlice2) // []int{1, 2, 3, 4}
Some time you may want to append an item to a slice only if a certain condition is matched or the Type
is not comparable, you can modify the AppendIfNotExists
as bellow:
func AppendIfNotExists[T any](slice []T, newItem T, checkExists func(T) bool) []T {
for _, s := range slice {
if checkExists(s) {
return slice
}
}
return append(slice, newItem)
}
Usage
type Thing struct {
ID int
Name string
}
var things = []Thing{
{ID: 1, Name: "one"},
{ID: 2, Name: "two"},
}
var thing3 = Thing{ID: 3, "three"}
var thing2 = Thing{ID: 2, Name: "Thing 2"}
var thingSlice1 = AppendIfNotExists(things, thing2, func (element Thing) bool {
return element.ID == thing2.ID
})
var thingSlice2 = AppendIfNotExists(things, thing3, func (element Thing) bool {
return element.ID == thing3.ID
})
fmt.Println(len(thingSlice1)) // 2
fmt.Println(len(thingSlice2)) // 3
Very Good !