I've moved development of this repo to github.com/schigh/slice. I'm going to use that repo to integrate slice functions using the current generics draft.
Some great and powerful gopher once said you shouldn't put this kind of stuff in a library because it's so simple. Until you have to write a contains method for the thousandth time.
Slices bolts on some generics-ish functionality to native Go data types when represented as a slice. Currently, the following types are supported:
Go Slice Type | Slices Type | xtra |
---|---|---|
[]int |
IntSlice |
benchmark |
[]int8 |
Int8Slice |
benchmark |
[]int16 |
Int16Slice |
benchmark |
[]int32 |
Int32Slice |
benchmark |
[]int64 |
Int64Slice |
benchmark |
[]uint |
UIntSlice |
benchmark |
[]uint8 |
UInt8Slice |
benchmark |
[]uint16 |
UInt16Slice |
benchmark |
[]uint32 |
UInt32Slice |
benchmark |
[]uint64 |
UInt64Slice |
benchmark |
[]uintptr |
UIntPtrSlice |
benchmark |
[]float32 |
Float32Slice |
benchmark |
[]float64 |
Float64Slice |
benchmark |
[]string |
StringSlice |
benchmark |
For the above types, the following operations are supported (x is the type in the slice):
Function | Description |
---|---|
IndexOf(x) | Get the first index of the searched element in the slice. If the element is not found, this function returns -1 |
Contains(x) | Test to see if slice contains element x |
Unique() | Returns unique items in slice. The first occurence of an element is the one that is kept |
SortAsc() | Sort the slice in ascending order |
SortDesc() | Sort the slice in descending order |
Reverse() | Reverses the slice |
Filter(func(x) bool) | Applies a filter function to every item in the slice and return all items where the filter returns true |
Map(func(x) x) | Iterate over the slice and replace the current value with the computed value |
Each(func(x)) | Iterate over the slice (non-mutating) |
TryEach(func(x) error) (int, error) | Iterate over the slice, and halt if an error is returned from user func. Return index of the failed member and the caught error |
IfEach(func(x) bool) (int, bool) | Iterate over the slice, and halt if false is returned from user func. Return the index of the element that caused the func to return false, and a bool that is true if every member of the slice returned true with the function applied. If all elements return true, the index returned is -1 |
Chunk(size) | Splits the slice into chunks of a specified size |
Value() | Returns the native type slice value |
package main
import (
"fmt"
"github.com/schigh/slices"
)
func main() {
orig := []string{"foo", "bar", "baz", "fizz", "buzz"}
startsWithF := slices.StringSlice(orig).Filter(func(s string) bool {
return len(s) > 0 && s[0] == 'f'
}).Value()
fmt.Println(startsWithF)
// ["foo", "fizz"]
}
package main
import (
"fmt"
"github.com/schigh/slices"
)
func main() {
orig := []int{9,1,6,2,3,4,3,4,5,7,8,9,0}
unique := slices.IntSlice(orig).Unique().SortDesc().Value()
fmt.Println(unique)
// [9,8,7,6,5,4,3,2,1,0]
}
Check out the GoDoc for more information.
The slice generator uses go:generate
to add slice functionality to your existing structs. You may choose which features you'd like to add by setting them in the generate command. For example:
//go:generate slices User map filter each
Will generate the Map
, Filter
, and Each
functionality (see below) on a User struct's slice type. You could also just say you want everything:
//go:generate slices User all
This will generate all functions produced by the tool.
Some generated functions take as their receiver a function that operates on every member of the slice. By default, these receivers use a function where each member is passed by reference. For example, the generated Filter
function on a User
struct:
// Filter evaluates every element in the slice, and returns all User
// instances where the eval function returns true
func (slice UserSlice) Filter(f func(*User) bool) UserSlice {
out := make([]User, 0, len(slice))
for _, v := range slice {
if f(&v) {
out = append(out, v)
}
}
return UserSlice(out)
}
The user-defined function receives a pointer to User
by default.
If instead you want your slice functions to operate by value, you can modify your generator tags like so:
//go:generate slices User filter(byvalue)
This would produce the following function:
// Filter evaluates every element in the slice, and returns all User
// instances where the eval function returns true
func (slice UserSlice) Filter(f func(User) bool) UserSlice {
out := make([]User, 0, len(slice))
for _, v := range slice {
if f(v) {
out = append(out, v)
}
}
return UserSlice(out)
}
You can also generate pointer slices by prepending an asterisk to your struct name in the go generate
directive, like so:
//go:generate slices *User all
This will generate a type called UserPtrSlice
, which is an alias of []*User
. All slice functions take a pointer receiver, for example:
func (slice UserPtrSlice) Map(f func(*User) *User) {
for i, v := range slice {
slice[i] = f(v)
}
}