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