Adding some new funky functions which I find useful

Created a Tuple struct as some of the new functions require you to return a new slice with two fields which is the result of the new functions

Created the Join, JoinProjection, Range, SumMap, Zip functions, ecah fuction is documented with how it works and had a unit test or maybe more
This commit is contained in:
Andy Long 2022-09-02 00:12:39 +01:00
parent 4042208482
commit 3066810f92
10 changed files with 229 additions and 0 deletions

1
.gitignore vendored
View file

@ -59,3 +59,4 @@ Temporary Items
.apdisk
docs/public
.trivycache/
.vscode/launch.json

39
join.go Normal file
View file

@ -0,0 +1,39 @@
package underscore
// Joins two slices together and returns a Tuple of [T, []P], the selectors allow you to pick the
// keys from your structure you would like to join the sets together with
func Join[T, P any, S comparable](
left []T,
right []P,
leftSelector func(T) S,
rightSelector func(P) S) []Tuple[T, []P] {
var results = make([]Tuple[T, []P], 0, len(left))
for _, l := range left {
var matches = Filter(right, func(r P) bool { return leftSelector(l) == rightSelector(r) })
var tuple = Tuple[T, []P]{Left: l, Right: matches}
results = append(results, tuple)
}
return results
}
// Joins two slices together and returns a []R where R is defined by the output of your projection
// function
// The selectors allow you to pick the keys from your structure you would like to join the sets
// together with.
// While the projection functions allows you to reformat joined datasets contains in the
// Tuple of [T, []P] into your own structure or type
func JoinProject[T, P, R any, S comparable](
left []T,
right []P,
leftSelector func(T) S,
rightSelector func(P) S,
projection func(Tuple[T, []P]) R) (results []R) {
for _, x := range Join(left, right, leftSelector, rightSelector) {
results = append(results, projection(x))
}
return results
}

30
join_test.go Normal file
View file

@ -0,0 +1,30 @@
package underscore_test
import (
"reflect"
"testing"
u "github.com/rjNemo/underscore"
)
func Test_Join_Can_Join_Two_Slices_Together(t *testing.T) {
one := u.Tuple[int, string]{Left: 1, Right: "One"}
two := u.Tuple[int, string]{Left: 2, Right: "Two"}
three := u.Tuple[int, string]{Left: 3, Right: "Three"}
var left = []u.Tuple[int, string]{one, two, three}
var right = []u.Tuple[int, string]{one, three, two, three, two, three}
selector := func(x u.Tuple[int, string]) int { return x.Left }
var joined = u.Join(left, right, selector, selector)
var want = []u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]{
{Left: one, Right: []u.Tuple[int, string]{one}},
{Left: two, Right: []u.Tuple[int, string]{two, two}},
{Left: three, Right: []u.Tuple[int, string]{three, three, three}},
}
if !reflect.DeepEqual(joined, want) {
t.Errorf("Expected to get %v but we got %v instead", want, joined)
}
}

16
range.go Normal file
View file

@ -0,0 +1,16 @@
package underscore
// Creates a sequence of numbers, i.e. u.Range(0, 3) = [0 1 2 3], while u.Range(3, 0) = [3 2 1 0]
func Range(start int, end int) (result []int) {
if start < end {
for i := start; i <= end; i++ {
result = append(result, i)
}
} else {
for i := start; i >= end; i-- {
result = append(result, i)
}
}
return result
}

24
range_test.go Normal file
View file

@ -0,0 +1,24 @@
package underscore_test
import (
"reflect"
"testing"
u "github.com/rjNemo/underscore"
)
func Test_Range_Creates_Slices(t *testing.T) {
range1 := u.Range(0, 5)
want1 := []int{0, 1, 2, 3, 4, 5}
if !reflect.DeepEqual(range1, want1) {
t.Errorf("Expected the result to be %v but we got %v", want1, range1)
}
range2 := u.Range(5, 0)
want2 := []int{5, 4, 3, 2, 1, 0}
if !reflect.DeepEqual(range2, want2) {
t.Errorf("Expected the result to be %v but we got %v", want2, range2)
}
}

10
sum.go
View file

@ -9,3 +9,13 @@ func Sum[T constraints.Ordered](values []T) (sum T) {
}
return sum
}
// Sums the values you select from your struct, basically a sort cut instead of
// having to perform a u.Map followed by a u.Sum
func SumMap[T any, R constraints.Ordered](list []T, selector func(T) R) (sum R) {
for _, v := range list {
sum += selector(v)
}
return sum
}

View file

@ -13,3 +13,21 @@ func TestSum(t *testing.T) {
assert.Equal(t, want, u.Sum(nums))
}
func TestSumMap(t *testing.T) {
nums := []u.Tuple[string, int]{
{"zero", 0},
{"one", 1},
{"two", 2},
{"three", 3},
{"four", 4},
{"five", 5},
{"six", 6},
{"seven", 7},
{"eight", 8},
{"nine", 9},
}
want := 45
assert.Equal(t, want, u.SumMap(nums, func(item u.Tuple[string, int]) int { return item.Right }))
}

6
tuple.go Normal file
View file

@ -0,0 +1,6 @@
package underscore
type Tuple[L, R any] struct {
Left L
Right R
}

20
zip.go Normal file
View file

@ -0,0 +1,20 @@
package underscore
// Zips two slices togther so all the elements of left slice are attached to the corresponding
// elements of the right slice, i.e. [one two three] [1 2 3 4] = [{one, 1} {two, 2} {three, 3}]
// the returned data will be the size of the smallest slice
func Zip[L any, R any](left []L, right []R) []Tuple[L, R] {
shortest := 0
if len(left) < len(right) {
shortest = len(left)
} else {
shortest = len(right)
}
results := make([]Tuple[L, R], shortest)
for i := 0; i < shortest; i++ {
results[i] = Tuple[L, R]{Left: left[i], Right: right[i]}
}
return results
}

65
zip_test.go Normal file
View file

@ -0,0 +1,65 @@
package underscore_test
import (
"reflect"
"testing"
u "github.com/rjNemo/underscore"
)
func Test_Zip_Can_Zip_Two_Equal_Sized_Slices(t *testing.T) {
left := []string{"Left 1", "Left 2", "Left 3"}
right := []int{1, 2, 3}
var zipped = u.Zip(left, right)
want := []u.Tuple[string, int]{
{Left: "Left 1", Right: 1},
{Left: "Left 2", Right: 2},
{Left: "Left 3", Right: 3},
}
if !reflect.DeepEqual(zipped, want) {
t.Errorf("Expected the result to be %v but we got %v", want, zipped)
}
}
func Test_Zip_Can_Zip_Two_Different_Sized_Slices_Left_Larger(t *testing.T) {
left := []string{"Left 1", "Left 2", "Left 3", "Left 4"}
right := []int{1, 2, 3}
var zipped = u.Zip(left, right)
if len(zipped) != 3 {
t.Errorf("Expected the result of Zip(left, right) to have a length of 3 but got %v", len(zipped))
}
want := []u.Tuple[string, int]{
{Left: "Left 1", Right: 1},
{Left: "Left 2", Right: 2},
{Left: "Left 3", Right: 3},
}
if !reflect.DeepEqual(zipped, want) {
t.Errorf("Expected the result to be %v but we got %v", want, zipped)
}
}
func Test_Zip_Can_Zip_Two_Different_Sized_Slices_Right_Larger(t *testing.T) {
left := []string{"Left 1", "Left 2", "Left 3"}
right := []int{1, 2, 3, 4}
var zipped = u.Zip(left, right)
if len(zipped) != 3 {
t.Errorf("Expected the result of Zip(left, right) to have a length of 3 but got %v", len(zipped))
}
want := []u.Tuple[string, int]{
{Left: "Left 1", Right: 1},
{Left: "Left 2", Right: 2},
{Left: "Left 3", Right: 3},
}
if !reflect.DeepEqual(zipped, want) {
t.Errorf("Expected the result to be %v but we got %v", want, zipped)
}
}