├── .github └── workflows │ └── test.yml ├── .gitignore ├── each.go ├── each_test.go ├── every.go ├── every_test.go ├── filter.go ├── filter_test.go ├── find.go ├── find_test.go ├── go.mod ├── go.sum ├── lib.go ├── map.go ├── map_test.go ├── readme.md ├── reduce.go ├── reduce_test.go ├── some.go ├── some_test.go ├── sort.go └── sort_test.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Install gotip 16 | run: | 17 | git clone --depth=1 https://go.googlesource.com/go $HOME/gotip 18 | cd $HOME/gotip/src 19 | ./make.bash 20 | echo "GOROOT=$HOME/gotip" >> $GITHUB_ENV 21 | echo "$HOME/gotip/bin:$PATH" >> $GITHUB_PATH 22 | - name: Check out code into the Go module directory 23 | uses: actions/checkout@v2 24 | 25 | - name: Get dependencies 26 | run: | 27 | go get -v -t -d ./... 28 | if [ -f Gopkg.toml ]; then 29 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 30 | dep ensure 31 | fi 32 | - name: Test 33 | run: go test -v ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /each.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Each[T any](d []T, h func(T) T) []T { 4 | r := make([]T, len(d)) 5 | for i, v := range d { 6 | r[i] = h(v) 7 | } 8 | return r 9 | } 10 | -------------------------------------------------------------------------------- /each_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestEach(t *testing.T) { 9 | c1 := Each([]int{1, 2, 3}, func(x int) int { 10 | return x + 10 11 | }) 12 | assert.Equal(t, []int{11, 12, 13}, c1) 13 | } 14 | -------------------------------------------------------------------------------- /every.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Every[T any](d []T, h func(T) bool) bool { 4 | for _, v := range d { 5 | if h(v) == false { 6 | return false 7 | } 8 | } 9 | return true 10 | } 11 | -------------------------------------------------------------------------------- /every_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestEvery(t *testing.T) { 9 | r := Every([]int{1, 2, 3}, func(x int) bool { 10 | return x < 100 11 | }) 12 | assert.Equal(t, true, r) 13 | } 14 | -------------------------------------------------------------------------------- /filter.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Filter[T any](d []T, h func(T) bool) []T { 4 | r := []T{} 5 | for _, v := range d { 6 | if h(v) { 7 | r = append(r, v) 8 | } 9 | } 10 | return r 11 | } 12 | -------------------------------------------------------------------------------- /filter_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestFilter(t *testing.T) { 9 | c1 := Filter([]int{1, 2, 3}, func(x int) bool { 10 | return x > 2 11 | }) 12 | assert.Equal(t, []int{3}, c1) 13 | } 14 | -------------------------------------------------------------------------------- /find.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Find[T any](d []T, h func(T) bool) *T { 4 | 5 | for _, v := range d { 6 | if h(v) { 7 | return &v 8 | } 9 | } 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /find_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestFind(t *testing.T) { 9 | c1 := Find([]int{1, 2, 3}, func(x int) bool { 10 | return x > 2 11 | }) 12 | if c1 != nil { 13 | assert.Equal(t, 3, *c1) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nicolasgere/go-underscore 2 | 3 | go 1.18 4 | 5 | require github.com/stretchr/testify v1.7.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.0 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 7 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 11 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 12 | -------------------------------------------------------------------------------- /lib.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | type Collection[T any] struct { 4 | Data []T 5 | } 6 | 7 | func New[T any](data []T) Collection[T] { 8 | c := Collection[T]{ 9 | Data: data, 10 | } 11 | return c 12 | } 13 | 14 | func (self *Collection[T]) Filter(handler func(T) bool) Collection[T] { 15 | tmp := []T{} 16 | for _, v := range self.Data { 17 | if handler(v) { 18 | tmp = append(tmp, v) 19 | } 20 | } 21 | return New[T](tmp) 22 | } 23 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Map[T1, T2 any](d []T1, h func(T1) T2) []T2 { 4 | r := make([]T2, len(d)) 5 | for i, v := range d { 6 | r[i] = h(v) 7 | } 8 | return r 9 | } 10 | -------------------------------------------------------------------------------- /map_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestMap(t *testing.T) { 9 | c1 := Map([]int{1, 2, 3}, func(x int) int64 { 10 | return int64(x) + 10 11 | }) 12 | assert.Equal(t, []int64{11, 12, 13}, c1) 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # go-underscore 3 | 4 | go-underscore is a utility-belt library for Golang that provides support for the usual functional suspects (each, map, reduce, filter...) without creating special container. This is largely inspeired by javascript package underscore. 5 | It rely on the new golang generic feature available with the 1.18 (still in development). 6 | 7 | 8 | ## Usage/Examples 9 | Install: go install github.com/nicolasgere/go-underscore 10 | 11 | Note: Generic are not available in golang yet, it would be available in golang 1.18. 12 | You can already try it with gotip. 13 | ```go 14 | package main 15 | import ( 16 | "fmt" 17 | underscore "github.com/nicolasgere/go-underscore" 18 | ) 19 | 20 | func main() { 21 | list := []int{1,2,3,4,5} 22 | listFiltered := underscore.Filter(list, func(e int) bool{ 23 | return e > 2 24 | }) 25 | fmt.Println(listFiltered) // Print: [3,4,5] 26 | listSquared := underscore.Each(listFiltered, func(value int) (res int){ 27 | return value * value 28 | }) 29 | fmt.Println(listSquared) // Print: [9,16,25] 30 | 31 | sum := underscore.Reduce(listSquared, func(value int, reducer int) (res int){ 32 | return value + reducer 33 | }, 0) 34 | fmt.Println(sum) // Print: 50 35 | } 36 | ``` 37 | 38 | 39 | ## Available function 40 | 41 | - [x] Each 42 | - [x] Map 43 | - [x] Filter 44 | - [x] Every 45 | - [x] Some 46 | - [x] Reduce 47 | - [x] Find 48 | - [x] Sort 49 | - [ ] Contains 50 | - [ ] Shuffle 51 | - [ ] First 52 | - [ ] Last -------------------------------------------------------------------------------- /reduce.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Reduce[T1, T2 any](d []T1, h func(T1, T2) T2, init T2) T2 { 4 | val := init 5 | for _, v := range d { 6 | val = h(v, val) 7 | } 8 | return val 9 | } 10 | -------------------------------------------------------------------------------- /reduce_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestReduce(t *testing.T) { 9 | c1 := Reduce([]int{1, 2, 3}, func(x int, val int) int { 10 | return val + x 11 | }, 0) 12 | assert.Equal(t, 6, c1) 13 | } 14 | -------------------------------------------------------------------------------- /some.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | func Some[T any](d []T, h func(T) bool) bool { 4 | for _, v := range d { 5 | if h(v) { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /some_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestSomeTrue(t *testing.T) { 9 | r := Some([]int{1, 2, 3}, func(x int) bool { 10 | return x == 2 11 | }) 12 | assert.Equal(t, true, r) 13 | } 14 | 15 | func TestSomeFalse(t *testing.T) { 16 | r := Some([]int{1, 2, 3}, func(x int) bool { 17 | return x > 100 18 | }) 19 | assert.Equal(t, false, r) 20 | } 21 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | func Sort[T any](d []T, h func(T, T) bool) []T { 8 | if len(d) < 2 { 9 | return d 10 | } 11 | 12 | left, right := 0, len(d)-1 13 | 14 | pivot := rand.Int() % len(d) 15 | 16 | d[pivot], d[right] = d[right], d[pivot] 17 | 18 | for i, _ := range d { 19 | if h(d[i], d[right]) { 20 | d[left], d[i] = d[i], d[left] 21 | left++ 22 | } 23 | } 24 | 25 | d[left], d[right] = d[right], d[left] 26 | 27 | Sort(d[:left], h) 28 | Sort(d[left+1:], h) 29 | 30 | return d 31 | } 32 | -------------------------------------------------------------------------------- /sort_test.go: -------------------------------------------------------------------------------- 1 | package underscore 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestSort(t *testing.T) { 9 | r := Sort([]int{34, 10, 2, 50}, func(x int, y int) bool { 10 | return x < y 11 | }) 12 | assert.Equal(t, []int{2, 10, 34, 50}, r) 13 | } 14 | --------------------------------------------------------------------------------