Skip to content

Commit

Permalink
feat: add CrossJoin function
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnDevitt committed Dec 6, 2024
1 parent efef0ff commit d1a43ff
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Supported helpers for slices:
- [IsSorted](#issorted)
- [IsSortedByKey](#issortedbykey)
- [Splice](#Splice)
- [CrossJoin](#CrossJoin)

Supported helpers for maps:

Expand Down Expand Up @@ -1039,7 +1040,20 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2")
// []string{"a", "b", "1", "2"}
```

[[play](https://go.dev/play/p/G5_GhkeSUBA)]
[[play](https://go.dev/play/p/wiG6XyBBu49)]

### CrossJoin

CrossJoin calculates the cartesian product of two lists. It returns a list of tuples where the first element includes the elements of the first parameter, and the second element contains the elements of the second parameter.

It returns an empty list if either, or both parameters are empty

```go
result := lo.CrossJoin([]string{"a", "b", "c"}, []int{1, 2, 3})
// [][2]interface{}{{"a", 1}, {"a", 2}, {"a", 3}, {"b", 1}, {"b", 2}, {"b", 3}, {"c", 1}, {"c", 2}, {"c", 3}
```

[[play](https://go.dev/play/p/2-DOGciKvAB)]

### Keys

Expand Down
24 changes: 24 additions & 0 deletions slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,3 +693,27 @@ func Splice[T any, Slice ~[]T](collection Slice, i int, elements ...T) Slice {

return append(append(append(output, collection[:i]...), elements...), collection[i:]...)
}

// CrossJoin calculates the cartesian product of two lists. It returns a list of
// tuples where the first element includes the elements of the first parameter, and
// the second element contains the elements of the second parameter.

// It returns an empty list if either, or both parameters are empty

// Play: https://go.dev/play/p/2-DOGciKvAB
func CrossJoin[T, U any](listOne []T, listTwo []U) [][2]interface{} {

if len(listOne) == 0 || len(listTwo) == 0 {
return make([][2]interface{}, 0)
}

cartesianProduct := make([][2]interface{}, 0, len(listOne)*len(listTwo))

for _, a := range listOne {
for _, b := range listTwo {
cartesianProduct = append(cartesianProduct, [2]interface{}{a, b})
}
}

return cartesianProduct
}
26 changes: 26 additions & 0 deletions slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1029,3 +1029,29 @@ func TestSplice(t *testing.T) {
nonempty := Splice(allStrings, 1, "1", "2")
is.IsType(nonempty, allStrings, "type preserved")
}

func TestCrossJoin(t *testing.T) {
t.Parallel()
is := assert.New(t)

listOne := []string{"a", "b", "c"}
listTwo := []int{1, 2, 3}

results := CrossJoin(make([][2]interface{}, 0), listTwo)
is.Equal(make([][2]interface{}, 0), results)

results = CrossJoin(listOne, make([][2]interface{}, 0))
is.Equal(make([][2]interface{}, 0), results)

results = CrossJoin(make([][2]interface{}, 0), make([][2]interface{}, 0))
is.Equal(make([][2]interface{}, 0), results)

results = CrossJoin([]string{"a"}, listTwo)
is.Equal([][2]interface{}{{"a", 1}, {"a", 2}, {"a", 3}}, results)

results = CrossJoin(listOne, []int{1})
is.Equal([][2]interface{}{{"a", 1}, {"b", 1}, {"c", 1}}, results)

results = CrossJoin(listOne, listTwo)
is.Equal([][2]interface{}{{"a", 1}, {"a", 2}, {"a", 3}, {"b", 1}, {"b", 2}, {"b", 3}, {"c", 1}, {"c", 2}, {"c", 3}}, results)
}

0 comments on commit d1a43ff

Please sign in to comment.