initial
This commit is contained in:
commit
9cd6b0fca8
14 changed files with 416 additions and 0 deletions
19
Readme.md
Normal file
19
Readme.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Collection with generics
|
||||||
|
|
||||||
|
Go >= 1.18 required.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
`go get https://go.neonxp.dev/collection@latest`
|
||||||
|
|
||||||
|
# Methods
|
||||||
|
|
||||||
|
|Method|Description|Example|
|
||||||
|
|:-----|:----------|------:|
|
||||||
|
|`Map`|Async map over slice|[example_map_test.go](./example_map_test.go)|
|
||||||
|
|`MapSync`|Sync map over slice|[example_map_test.go](./example_map_test.go)|
|
||||||
|
|`Each`|Async call cb over each element|[example_each_test.go](./example_each_test.go)|
|
||||||
|
|`MapEach`|Sync call cb over each element|[example_each_test.go](./example_each_test.go)|
|
||||||
|
|`Filter`|Returns filtered elements async|TODO|
|
||||||
|
|`FilterSync`|Returns filtered elements|TODO|
|
||||||
|
|`Reduce`|Produce one single result from a sequence of elements|TODO|
|
23
each.go
Normal file
23
each.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
func EachSync[T any](collection []T, cb func(item T, idx int)) {
|
||||||
|
for i, v := range collection {
|
||||||
|
cb(v, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Each[T any](collection []T, cb func(item T, idx int)) {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(collection))
|
||||||
|
for i, v := range collection {
|
||||||
|
func(i int, v T) {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
cb(v, i)
|
||||||
|
}()
|
||||||
|
}(i, v)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
30
each_test.go
Normal file
30
each_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEachSync(t *testing.T) {
|
||||||
|
collection := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
want := 21
|
||||||
|
sum := 0
|
||||||
|
EachSync(collection, func(v int, _ int) {
|
||||||
|
sum += v
|
||||||
|
})
|
||||||
|
if sum != want {
|
||||||
|
t.Errorf("Expected %d, got %d", want, sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEach(t *testing.T) {
|
||||||
|
collection := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
want := int64(21)
|
||||||
|
sum := int64(0)
|
||||||
|
Each(collection, func(v int, _ int) {
|
||||||
|
atomic.AddInt64(&sum, int64(v))
|
||||||
|
})
|
||||||
|
if sum != want {
|
||||||
|
t.Errorf("Expected %d, got %d", want, sum)
|
||||||
|
}
|
||||||
|
}
|
17
example_each_test.go
Normal file
17
example_each_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleEach() {
|
||||||
|
collection := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
Each(collection, func(v int, idx int) {
|
||||||
|
fmt.Printf("Element %d: %d\n", idx, v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEachSync() {
|
||||||
|
collection := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
EachSync(collection, func(v int, idx int) {
|
||||||
|
fmt.Printf("Element %d: %d\n", idx, v)
|
||||||
|
})
|
||||||
|
}
|
26
example_map_test.go
Normal file
26
example_map_test.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleMap() {
|
||||||
|
collection := []int{1, 2, 3, 4, 5}
|
||||||
|
cb := func(v int, idx int) string {
|
||||||
|
return fmt.Sprintf("[%d]", v)
|
||||||
|
}
|
||||||
|
result := Map(collection, cb)
|
||||||
|
fmt.Println(strings.Join(result, "_"))
|
||||||
|
// Output: [1]_[2]_[3]_[4]_[5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMapSync() {
|
||||||
|
collection := []int{1, 2, 3, 4, 5}
|
||||||
|
cb := func(v int, idx int) string {
|
||||||
|
return fmt.Sprintf("[%d]", v)
|
||||||
|
}
|
||||||
|
result := MapSync(collection, cb)
|
||||||
|
fmt.Println(strings.Join(result, "_"))
|
||||||
|
// Output: [1]_[2]_[3]_[4]_[5]
|
||||||
|
}
|
34
filter.go
Normal file
34
filter.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
func FilterSync[T any](collection []T, filter func(item T, idx int) bool) []T {
|
||||||
|
var result []T
|
||||||
|
for i, v := range collection {
|
||||||
|
if filter(v, i) {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter[T any](collection []T, filter func(item T, idx int) bool) []T {
|
||||||
|
var result []T
|
||||||
|
mu := sync.Mutex{}
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(collection))
|
||||||
|
for i, v := range collection {
|
||||||
|
func(v T, i int) {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if filter(v, i) {
|
||||||
|
mu.Lock()
|
||||||
|
result = append(result, v)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}(v, i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return result
|
||||||
|
}
|
66
filter_test.go
Normal file
66
filter_test.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterSync(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
collection []int
|
||||||
|
filter func(item int, idx int) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "odds",
|
||||||
|
args: args{
|
||||||
|
collection: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
|
filter: func(item int, idx int) bool {
|
||||||
|
return item%2 == 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []int{2, 4, 6, 8, 10},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := FilterSync(tt.args.collection, tt.args.filter); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Filter() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
collection []int
|
||||||
|
filter func(item int, idx int) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "odds count",
|
||||||
|
args: args{
|
||||||
|
collection: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
|
filter: func(item int, idx int) bool {
|
||||||
|
return item%2 == 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := Filter(tt.args.collection, tt.args.filter); len(got) != tt.want {
|
||||||
|
t.Errorf("FilterParallel() returned %v elements, want %v", len(got), tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module go.neonxp.dev/collection
|
||||||
|
|
||||||
|
go 1.18
|
27
map.go
Normal file
27
map.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
func MapSync[T any, R any](collection []T, cb func(item T, idx int) R) []R {
|
||||||
|
result := make([]R, len(collection))
|
||||||
|
for i, v := range collection {
|
||||||
|
result[i] = cb(v, i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map[T any, R any](collection []T, cb func(item T, idx int) R) []R {
|
||||||
|
result := make([]R, len(collection))
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(collection))
|
||||||
|
for i, v := range collection {
|
||||||
|
func(v T, i int) {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
result[i] = cb(v, i)
|
||||||
|
}()
|
||||||
|
}(v, i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return result
|
||||||
|
}
|
78
map_test.go
Normal file
78
map_test.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapSync(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
collection []int
|
||||||
|
cb func(int, int) int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "multiple",
|
||||||
|
args: args{
|
||||||
|
collection: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
|
cb: func(item int, idx int) int {
|
||||||
|
return item * idx
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []int{0, 2, 6, 12, 20, 30, 42, 56, 72, 90},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := MapSync(tt.args.collection, tt.args.cb); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Map() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
collection []int
|
||||||
|
cb func(int, int) string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "counter",
|
||||||
|
args: args{
|
||||||
|
collection: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
|
cb: func(item int, idx int) string {
|
||||||
|
return fmt.Sprintf("%dth element is %d", idx, item)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []string{
|
||||||
|
"0th element is 1",
|
||||||
|
"1th element is 2",
|
||||||
|
"2th element is 3",
|
||||||
|
"3th element is 4",
|
||||||
|
"4th element is 5",
|
||||||
|
"5th element is 6",
|
||||||
|
"6th element is 7",
|
||||||
|
"7th element is 8",
|
||||||
|
"8th element is 9",
|
||||||
|
"9th element is 10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := Map(tt.args.collection, tt.args.cb); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Map() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
8
reduce.go
Normal file
8
reduce.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
func Reduce[T any, R any](collection []T, cb func(previous R, current T, idx int) R, accumulator R) R {
|
||||||
|
for i, v := range collection {
|
||||||
|
accumulator = cb(accumulator, v, i)
|
||||||
|
}
|
||||||
|
return accumulator
|
||||||
|
}
|
37
reduce_test.go
Normal file
37
reduce_test.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReduce(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
collection []int
|
||||||
|
cb func(previous int, current int, idx int) int
|
||||||
|
accumulator int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Sum",
|
||||||
|
args: args{
|
||||||
|
collection: []int{1, 2, 3, 4, 5, 6},
|
||||||
|
cb: func(previous, current, idx int) int {
|
||||||
|
return previous + current
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: 21,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := Reduce(tt.args.collection, tt.args.cb, tt.args.accumulator); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Reduce() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
12
stack.go
Normal file
12
stack.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
func Push[T any](collection []T, element T) []T {
|
||||||
|
return append(collection, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pop[T any](collection []T) ([]T, T) {
|
||||||
|
if len(collection) == 0 {
|
||||||
|
return collection, *new(T)
|
||||||
|
}
|
||||||
|
return collection[:len(collection)-1], collection[len(collection)-1]
|
||||||
|
}
|
36
stack_test.go
Normal file
36
stack_test.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPushPop(t *testing.T) {
|
||||||
|
collection := []int{}
|
||||||
|
collection = Push(collection, 1)
|
||||||
|
collection = Push(collection, 2)
|
||||||
|
collection = Push(collection, 3)
|
||||||
|
want := []int{1, 2, 3}
|
||||||
|
if !reflect.DeepEqual(collection, want) {
|
||||||
|
t.Errorf("Want %+v, but got %+v", want, collection)
|
||||||
|
}
|
||||||
|
collection, e := Pop(collection)
|
||||||
|
if e != 3 {
|
||||||
|
t.Errorf("Want 3, but got %d", e)
|
||||||
|
}
|
||||||
|
collection, e = Pop(collection)
|
||||||
|
if e != 2 {
|
||||||
|
t.Errorf("Want 2, but got %d", e)
|
||||||
|
}
|
||||||
|
collection, e = Pop(collection)
|
||||||
|
if e != 1 {
|
||||||
|
t.Errorf("Want 1, but got %d", e)
|
||||||
|
}
|
||||||
|
collection, e = Pop(collection)
|
||||||
|
if e != 0 {
|
||||||
|
t.Errorf("Want 0, but got %d", e)
|
||||||
|
}
|
||||||
|
if len(collection) != 0 {
|
||||||
|
t.Errorf("Collection must be empty, but got %+v (len = %d)", collection, len(collection))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue