This commit is contained in:
Alexander Kiryukhin 2022-04-07 21:36:40 +03:00
commit 9cd6b0fca8
No known key found for this signature in database
GPG key ID: 6DF7A2910D0699E9
14 changed files with 416 additions and 0 deletions

19
Readme.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
module go.neonxp.dev/collection
go 1.18

27
map.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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))
}
}