mirror of
https://github.com/rjNemo/underscore
synced 2026-06-06 02:26:42 +00:00
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:
parent
4042208482
commit
3066810f92
10 changed files with 229 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -59,3 +59,4 @@ Temporary Items
|
|||
.apdisk
|
||||
docs/public
|
||||
.trivycache/
|
||||
.vscode/launch.json
|
||||
|
|
|
|||
39
join.go
Normal file
39
join.go
Normal 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
30
join_test.go
Normal 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
16
range.go
Normal 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
24
range_test.go
Normal 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
10
sum.go
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
18
sum_test.go
18
sum_test.go
|
|
@ -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
6
tuple.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package underscore
|
||||
|
||||
type Tuple[L, R any] struct {
|
||||
Left L
|
||||
Right R
|
||||
}
|
||||
20
zip.go
Normal file
20
zip.go
Normal 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
65
zip_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue