Skip to content

Commit 0bc3a54

Browse files
authored
Adding some new funky functions (#32)
* 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 * Added in an OrderBy function * Documentation comment for OrderBy which I missed out * Adding a Unit test for JoinProject function Updated the comments on the Join & OrderBy functions so they make a little more sense. Covered an extra test case with the Join test, where the left set has more data than the right and so the Right handside array of the join is empty
1 parent 4042208 commit 0bc3a54

File tree

12 files changed

+300
-0
lines changed

12 files changed

+300
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,4 @@ Temporary Items
5959
.apdisk
6060
docs/public
6161
.trivycache/
62+
.vscode/launch.json

join.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package underscore
2+
3+
// Joins two slices together and returns a Tuple of [T, []P], the selectors allow you to pick the
4+
// keys you want to use from your struct's to join the sets together
5+
func Join[T, P any, S comparable](
6+
left []T,
7+
right []P,
8+
leftSelector func(T) S,
9+
rightSelector func(P) S) []Tuple[T, []P] {
10+
11+
var results = make([]Tuple[T, []P], 0, len(left))
12+
for _, l := range left {
13+
var matches = Filter(right, func(r P) bool { return leftSelector(l) == rightSelector(r) })
14+
var tuple = Tuple[T, []P]{Left: l, Right: matches}
15+
results = append(results, tuple)
16+
}
17+
18+
return results
19+
}
20+
21+
// Joins two slices together and returns a []O where O is defined by the output
22+
// of your projection function
23+
// The selectors allow you to pick the keys from your structure to use as the join keys
24+
// While the projection functions allows you to reformat joined datasets
25+
// (Tuple of [T, []P]) into your own struct or type
26+
func JoinProject[L, R, O any, S comparable](
27+
left []L,
28+
right []R,
29+
leftSelector func(L) S,
30+
rightSelector func(R) S,
31+
projection func(Tuple[L, []R]) O) (results []O) {
32+
33+
for _, x := range Join(left, right, leftSelector, rightSelector) {
34+
results = append(results, projection(x))
35+
}
36+
37+
return results
38+
}

join_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package underscore_test
2+
3+
import (
4+
"testing"
5+
6+
u "github.com/rjNemo/underscore"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
var zero = u.Tuple[int, string]{Left: 0, Right: "Zero"}
11+
var one = u.Tuple[int, string]{Left: 1, Right: "One"}
12+
var two = u.Tuple[int, string]{Left: 2, Right: "Two"}
13+
var three = u.Tuple[int, string]{Left: 3, Right: "Three"}
14+
15+
func Test_Join_Can_Join_Two_Slices_Together(t *testing.T) {
16+
var left = []u.Tuple[int, string]{zero, one, two, three}
17+
var right = []u.Tuple[int, string]{one, three, two, three, two, three}
18+
19+
selector := func(x u.Tuple[int, string]) int { return x.Left }
20+
21+
var joined = u.Join(left, right, selector, selector)
22+
var want = []u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]{
23+
{Left: zero, Right: nil},
24+
{Left: one, Right: []u.Tuple[int, string]{one}},
25+
{Left: two, Right: []u.Tuple[int, string]{two, two}},
26+
{Left: three, Right: []u.Tuple[int, string]{three, three, three}},
27+
}
28+
29+
assert.Equal(t, want, joined)
30+
}
31+
32+
func Test_Join_Can_Join_and_Project_Two_Slices_Together(t *testing.T) {
33+
var left = []u.Tuple[int, string]{zero, one, two, three}
34+
var right = []u.Tuple[int, string]{one, three, two, three, two, three}
35+
36+
selector := func(x u.Tuple[int, string]) int { return x.Left }
37+
project := func(x u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]) int {
38+
return len(x.Right) // projecting to a could of how many
39+
}
40+
41+
var joined = u.JoinProject(left, right, selector, selector, project)
42+
var want = []int{0, 1, 2, 3}
43+
44+
assert.Equal(t, want, joined)
45+
}

orderBy.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package underscore
2+
3+
// Orders a slice by a field value within a struct, the predicate allows you
4+
// to pick the fields you want to orderBy. Use > for ASC or < for DESC
5+
// func (left Person, right Person) bool { return left.Age > right.Age }
6+
func OrderBy[T any](list []T, predicate func(T, T) bool) []T {
7+
swaps := true
8+
var tmp T
9+
10+
//todo: replace with a faster algorithm, this one is pretty simple
11+
for swaps {
12+
swaps = false
13+
14+
for i := 0; i < len(list)-1; i++ {
15+
if predicate(list[i], list[i+1]) {
16+
swaps = true
17+
tmp = list[i]
18+
19+
list[i] = list[i+1]
20+
list[i+1] = tmp
21+
}
22+
}
23+
}
24+
25+
return list
26+
}

orderBy_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package underscore_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
u "github.com/rjNemo/underscore"
9+
)
10+
11+
func Test_OrderBy_Asc(t *testing.T) {
12+
set := u.Range(5, 0)
13+
want := u.Range(0, 5)
14+
15+
result := u.OrderBy(set, func(left int, right int) bool {
16+
return left > right
17+
})
18+
19+
assert.Equal(t, want, result)
20+
}
21+
22+
func Test_OrderBy_Desc(t *testing.T) {
23+
set := u.Range(0, 5)
24+
want := u.Range(5, 0)
25+
26+
result := u.OrderBy(set, func(left int, right int) bool {
27+
return left < right
28+
})
29+
30+
assert.Equal(t, want, result)
31+
}

range.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package underscore
2+
3+
// Creates a sequence of numbers, i.e. u.Range(0, 3) = [0 1 2 3], while u.Range(3, 0) = [3 2 1 0]
4+
func Range(start int, end int) (result []int) {
5+
if start < end {
6+
for i := start; i <= end; i++ {
7+
result = append(result, i)
8+
}
9+
} else {
10+
for i := start; i >= end; i-- {
11+
result = append(result, i)
12+
}
13+
}
14+
15+
return result
16+
}

range_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package underscore_test
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
u "github.com/rjNemo/underscore"
8+
)
9+
10+
func Test_Range_Creates_Slices(t *testing.T) {
11+
range1 := u.Range(0, 5)
12+
want1 := []int{0, 1, 2, 3, 4, 5}
13+
14+
if !reflect.DeepEqual(range1, want1) {
15+
t.Errorf("Expected the result to be %v but we got %v", want1, range1)
16+
}
17+
18+
range2 := u.Range(5, 0)
19+
want2 := []int{5, 4, 3, 2, 1, 0}
20+
21+
if !reflect.DeepEqual(range2, want2) {
22+
t.Errorf("Expected the result to be %v but we got %v", want2, range2)
23+
}
24+
}

sum.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ func Sum[T constraints.Ordered](values []T) (sum T) {
99
}
1010
return sum
1111
}
12+
13+
// Sums the values you select from your struct, basically a sort cut instead of
14+
// having to perform a u.Map followed by a u.Sum
15+
func SumMap[T any, R constraints.Ordered](list []T, selector func(T) R) (sum R) {
16+
for _, v := range list {
17+
sum += selector(v)
18+
}
19+
20+
return sum
21+
}

sum_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,21 @@ func TestSum(t *testing.T) {
1313

1414
assert.Equal(t, want, u.Sum(nums))
1515
}
16+
17+
func TestSumMap(t *testing.T) {
18+
nums := []u.Tuple[string, int]{
19+
{"zero", 0},
20+
{"one", 1},
21+
{"two", 2},
22+
{"three", 3},
23+
{"four", 4},
24+
{"five", 5},
25+
{"six", 6},
26+
{"seven", 7},
27+
{"eight", 8},
28+
{"nine", 9},
29+
}
30+
want := 45
31+
32+
assert.Equal(t, want, u.SumMap(nums, func(item u.Tuple[string, int]) int { return item.Right }))
33+
}

tuple.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package underscore
2+
3+
type Tuple[L, R any] struct {
4+
Left L
5+
Right R
6+
}

0 commit comments

Comments
 (0)