diff --git a/README.md b/README.md index 73c33223..019de08a 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ Supported helpers for slices: - [IsSorted](#issorted) - [IsSortedByKey](#issortedbykey) - [Splice](#Splice) +- [CrossJoin](#CrossJoin) Supported helpers for maps: @@ -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 diff --git a/slice.go b/slice.go index d2d3fd84..dfa48a8b 100644 --- a/slice.go +++ b/slice.go @@ -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 +} diff --git a/slice_test.go b/slice_test.go index 9f923eea..12061580 100644 --- a/slice_test.go +++ b/slice_test.go @@ -1029,3 +1029,34 @@ 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} + emptyList := make([][2]any, 0) + mixedList := []any{9.6, 4, "foobar"} + + results := CrossJoin(emptyList, listTwo) + is.Equal(emptyList, results) + + results = CrossJoin(listOne, emptyList) + is.Equal(emptyList, results) + + results = CrossJoin(emptyList, emptyList) + is.Equal(emptyList, results) + + results = CrossJoin([]string{"a"}, listTwo) + is.Equal([][2]any{{"a", 1}, {"a", 2}, {"a", 3}}, results) + + results = CrossJoin(listOne, []int{1}) + is.Equal([][2]any{{"a", 1}, {"b", 1}, {"c", 1}}, results) + + results = CrossJoin(listOne, listTwo) + is.Equal([][2]any{{"a", 1}, {"a", 2}, {"a", 3}, {"b", 1}, {"b", 2}, {"b", 3}, {"c", 1}, {"c", 2}, {"c", 3}}, results) + + results = CrossJoin(listOne, mixedList) + is.Equal([][2]any{{"a", 9.6}, {"a", 4}, {"a", "foobar"}, {"b", 9.6}, {"b", 4}, {"b", "foobar"}, {"c", 9.6}, {"c", 4}, {"c", "foobar"}}, results) +}