diff --git a/README.md b/README.md index a590d17..36702aa 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,6 @@ Supported helpers for slices: - [IsSorted](#issorted) - [IsSortedByKey](#issortedbykey) - [Splice](#Splice) -- [CrossJoin](#CrossJoin) Supported helpers for maps: @@ -178,6 +177,8 @@ Supported helpers for tuples: - [ZipBy2 -> ZipBy9](#zipby2---zipby9) - [Unzip2 -> Unzip9](#unzip2---unzip9) - [UnzipBy2 -> UnzipBy9](#unzipby2---unzipby9) +- [CrossJoin2 -> CrossJoin2](#crossjoin2---crossjoin9) +- [CrossJoinBy2 -> CrossJoinBy2](#crossjoinby2---crossjoinby9) Supported helpers for time and duration: @@ -1064,19 +1065,6 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2") [[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 Creates a slice of the map keys. @@ -1710,6 +1698,36 @@ a, b := lo.UnzipBy2([]string{"hello", "john", "doe"}, func(str string) (string, // []int{5, 4, 3} ``` +### CrossJoin2 -> CrossJoin9 + +Combines every items from one list with every items from others. It is the cartesian product of lists received as arguments. It returns an empty list if a list is empty. + +```go +result := lo.CrossJoin2([]string{"hello", "john", "doe"}, []int{1, 2}) +// lo.Tuple2{"hello", 1} +// lo.Tuple2{"hello", 2} +// lo.Tuple2{"john", 1} +// lo.Tuple2{"john", 2} +// lo.Tuple2{"doe", 1} +// lo.Tuple2{"doe", 2} +``` + +### CrossJoinBy2 -> CrossJoinBy9 + +Combines every items from one list with every items from others. It is the cartesian product of lists received as arguments. The project function is used to create the output values. It returns an empty list if a list is empty. + +```go +result := lo.CrossJoinBy2([]string{"hello", "john", "doe"}, []int{1, 2}, func(a A, b B) string { + return fmt.Sprintf("%s - %d", a, b) +}) +// "hello - 1" +// "hello - 2" +// "john - 1" +// "john - 2" +// "doe - 1" +// "doe - 2" +``` + ### Duration Returns the time taken to execute a function. diff --git a/slice.go b/slice.go index 22464b6..7e6226a 100644 --- a/slice.go +++ b/slice.go @@ -708,27 +708,3 @@ 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 3727061..ec331ab 100644 --- a/slice_test.go +++ b/slice_test.go @@ -1046,34 +1046,3 @@ 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) -} diff --git a/tuples.go b/tuples.go index 18a0300..e355d0c 100644 --- a/tuples.go +++ b/tuples.go @@ -867,3 +867,283 @@ func UnzipBy9[In any, A any, B any, C any, D any, E any, F any, G any, H any, I return r1, r2, r3, r4, r5, r6, r7, r8, r9 } + +// CrossJoin2 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin2[A, B any](listA []A, listB []B) []Tuple2[A, B] { + return CrossJoinBy2(listA, listB, T2[A, B]) +} + +// CrossJoin3 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin3[A, B, C any](listA []A, listB []B, listC []C) []Tuple3[A, B, C] { + return CrossJoinBy3(listA, listB, listC, T3[A, B, C]) +} + +// CrossJoin4 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin4[A, B, C, D any](listA []A, listB []B, listC []C, listD []D) []Tuple4[A, B, C, D] { + return CrossJoinBy4(listA, listB, listC, listD, T4[A, B, C, D]) +} + +// CrossJoin5 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin5[A, B, C, D, E any](listA []A, listB []B, listC []C, listD []D, listE []E) []Tuple5[A, B, C, D, E] { + return CrossJoinBy5(listA, listB, listC, listD, listE, T5[A, B, C, D, E]) +} + +// CrossJoin6 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin6[A, B, C, D, E, F any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F) []Tuple6[A, B, C, D, E, F] { + return CrossJoinBy6(listA, listB, listC, listD, listE, listF, T6[A, B, C, D, E, F]) +} + +// CrossJoin7 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin7[A, B, C, D, E, F, G any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G) []Tuple7[A, B, C, D, E, F, G] { + return CrossJoinBy7(listA, listB, listC, listD, listE, listF, listG, T7[A, B, C, D, E, F, G]) +} + +// CrossJoin8 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin8[A, B, C, D, E, F, G, H any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H) []Tuple8[A, B, C, D, E, F, G, H] { + return CrossJoinBy8(listA, listB, listC, listD, listE, listF, listG, listH, T8[A, B, C, D, E, F, G, H]) +} + +// CrossJoin9 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. +// It returns an empty list if a list is empty. +func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I) []Tuple9[A, B, C, D, E, F, G, H, I] { + return CrossJoinBy9(listA, listB, listC, listD, listE, listF, listG, listH, listI, T9[A, B, C, D, E, F, G, H, I]) +} + +// CrossJoinBy2 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Out) []Out { + size := len(listA) * len(listB) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + result = append(result, project(a, b)) + } + } + + return result +} + +// CrossJoinBy3 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project func(a A, b B, c C) Out) []Out { + size := len(listA) * len(listB) * len(listC) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + result = append(result, project(a, b, c)) + } + } + } + + return result +} + +// CrossJoinBy4 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, project func(a A, b B, c C, d D) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + result = append(result, project(a, b, c, d)) + } + } + } + } + + return result +} + +// CrossJoinBy5 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, project func(a A, b B, c C, d D, e E) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + for _, e := range listE { + result = append(result, project(a, b, c, d, e)) + } + } + } + } + } + + return result +} + +// CrossJoinBy6 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, project func(a A, b B, c C, d D, e E, f F) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + for _, e := range listE { + for _, f := range listF { + result = append(result, project(a, b, c, d, e, f)) + } + } + } + } + } + } + + return result +} + +// CrossJoinBy7 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, project func(a A, b B, c C, d D, e E, f F, g G) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + for _, e := range listE { + for _, f := range listF { + for _, g := range listG { + result = append(result, project(a, b, c, d, e, f, g)) + } + } + } + } + } + } + } + + return result +} + +// CrossJoinBy8 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, project func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + for _, e := range listE { + for _, f := range listF { + for _, g := range listG { + for _, h := range listH { + result = append(result, project(a, b, c, d, e, f, g, h)) + } + } + } + } + } + } + } + } + + return result +} + +// CrossJoinBy9 combines every items from one list with every items from others. +// It is the cartesian product of lists received as arguments. The project function +// is used to create the output values. +// It returns an empty list if a list is empty. +func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, project func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out { + size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) * len(listI) + if size == 0 { + return []Out{} + } + + result := make([]Out, 0, size) + + for _, a := range listA { + for _, b := range listB { + for _, c := range listC { + for _, d := range listD { + for _, e := range listE { + for _, f := range listF { + for _, g := range listG { + for _, h := range listH { + for _, i := range listI { + result = append(result, project(a, b, c, d, e, f, g, h, i)) + } + } + } + } + } + } + } + } + } + + return result +} diff --git a/tuples_example_test.go b/tuples_example_test.go index b8eb2e9..fec30a5 100644 --- a/tuples_example_test.go +++ b/tuples_example_test.go @@ -195,3 +195,387 @@ func ExampleUnzip9() { fmt.Printf("%v %v %v %v %v %v %v %v %v", a, b, c, d, e, f, g, h, i) // Output: [hello] [2] [true] [{bar}] [4.2] [plop] [false] [42] [hello world] } + +func ExampleCrossJoin2() { + result := CrossJoin2([]string{"a", "b"}, []int{1, 2, 3, 4}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1} + // {a 2} + // {a 3} + // {a 4} + // {b 1} + // {b 2} + // {b 3} + // {b 4} +} + +func ExampleCrossJoin3() { + result := CrossJoin3([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true} + // {a 1 false} + // {a 2 true} + // {a 2 false} + // {a 3 true} + // {a 3 false} + // {a 4 true} + // {a 4 false} + // {b 1 true} + // {b 1 false} + // {b 2 true} + // {b 2 false} + // {b 3 true} + // {b 3 false} + // {b 4 true} + // {b 4 false} +} + +func ExampleCrossJoin4() { + result := CrossJoin4([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar}} + // {a 1 false {bar}} + // {a 2 true {bar}} + // {a 2 false {bar}} + // {a 3 true {bar}} + // {a 3 false {bar}} + // {a 4 true {bar}} + // {a 4 false {bar}} + // {b 1 true {bar}} + // {b 1 false {bar}} + // {b 2 true {bar}} + // {b 2 false {bar}} + // {b 3 true {bar}} + // {b 3 false {bar}} + // {b 4 true {bar}} + // {b 4 false {bar}} +} + +func ExampleCrossJoin5() { + result := CrossJoin5([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar} 4.2} + // {a 1 false {bar} 4.2} + // {a 2 true {bar} 4.2} + // {a 2 false {bar} 4.2} + // {a 3 true {bar} 4.2} + // {a 3 false {bar} 4.2} + // {a 4 true {bar} 4.2} + // {a 4 false {bar} 4.2} + // {b 1 true {bar} 4.2} + // {b 1 false {bar} 4.2} + // {b 2 true {bar} 4.2} + // {b 2 false {bar} 4.2} + // {b 3 true {bar} 4.2} + // {b 3 false {bar} 4.2} + // {b 4 true {bar} 4.2} + // {b 4 false {bar} 4.2} +} + +func ExampleCrossJoin6() { + result := CrossJoin6([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar} 4.2 plop} + // {a 1 false {bar} 4.2 plop} + // {a 2 true {bar} 4.2 plop} + // {a 2 false {bar} 4.2 plop} + // {a 3 true {bar} 4.2 plop} + // {a 3 false {bar} 4.2 plop} + // {a 4 true {bar} 4.2 plop} + // {a 4 false {bar} 4.2 plop} + // {b 1 true {bar} 4.2 plop} + // {b 1 false {bar} 4.2 plop} + // {b 2 true {bar} 4.2 plop} + // {b 2 false {bar} 4.2 plop} + // {b 3 true {bar} 4.2 plop} + // {b 3 false {bar} 4.2 plop} + // {b 4 true {bar} 4.2 plop} + // {b 4 false {bar} 4.2 plop} +} + +func ExampleCrossJoin7() { + result := CrossJoin7([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar} 4.2 plop false} + // {a 1 false {bar} 4.2 plop false} + // {a 2 true {bar} 4.2 plop false} + // {a 2 false {bar} 4.2 plop false} + // {a 3 true {bar} 4.2 plop false} + // {a 3 false {bar} 4.2 plop false} + // {a 4 true {bar} 4.2 plop false} + // {a 4 false {bar} 4.2 plop false} + // {b 1 true {bar} 4.2 plop false} + // {b 1 false {bar} 4.2 plop false} + // {b 2 true {bar} 4.2 plop false} + // {b 2 false {bar} 4.2 plop false} + // {b 3 true {bar} 4.2 plop false} + // {b 3 false {bar} 4.2 plop false} + // {b 4 true {bar} 4.2 plop false} + // {b 4 false {bar} 4.2 plop false} +} + +func ExampleCrossJoin8() { + result := CrossJoin8([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}, []int{42}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar} 4.2 plop false 42} + // {a 1 false {bar} 4.2 plop false 42} + // {a 2 true {bar} 4.2 plop false 42} + // {a 2 false {bar} 4.2 plop false 42} + // {a 3 true {bar} 4.2 plop false 42} + // {a 3 false {bar} 4.2 plop false 42} + // {a 4 true {bar} 4.2 plop false 42} + // {a 4 false {bar} 4.2 plop false 42} + // {b 1 true {bar} 4.2 plop false 42} + // {b 1 false {bar} 4.2 plop false 42} + // {b 2 true {bar} 4.2 plop false 42} + // {b 2 false {bar} 4.2 plop false 42} + // {b 3 true {bar} 4.2 plop false 42} + // {b 3 false {bar} 4.2 plop false 42} + // {b 4 true {bar} 4.2 plop false 42} + // {b 4 false {bar} 4.2 plop false 42} +} + +func ExampleCrossJoin9() { + result := CrossJoin9([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}, []int{42}, []string{"hello world"}) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // {a 1 true {bar} 4.2 plop false 42 hello world} + // {a 1 false {bar} 4.2 plop false 42 hello world} + // {a 2 true {bar} 4.2 plop false 42 hello world} + // {a 2 false {bar} 4.2 plop false 42 hello world} + // {a 3 true {bar} 4.2 plop false 42 hello world} + // {a 3 false {bar} 4.2 plop false 42 hello world} + // {a 4 true {bar} 4.2 plop false 42 hello world} + // {a 4 false {bar} 4.2 plop false 42 hello world} + // {b 1 true {bar} 4.2 plop false 42 hello world} + // {b 1 false {bar} 4.2 plop false 42 hello world} + // {b 2 true {bar} 4.2 plop false 42 hello world} + // {b 2 false {bar} 4.2 plop false 42 hello world} + // {b 3 true {bar} 4.2 plop false 42 hello world} + // {b 3 false {bar} 4.2 plop false 42 hello world} + // {b 4 true {bar} 4.2 plop false 42 hello world} + // {b 4 false {bar} 4.2 plop false 42 hello world} +} + +func ExampleCrossJoinBy2() { + result := CrossJoinBy2([]string{"a", "b"}, []int{1, 2, 3, 4}, func(a string, b int) string { + return fmt.Sprintf("%v-%v", a, b) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1 + // a-2 + // a-3 + // a-4 + // b-1 + // b-2 + // b-3 + // b-4 +} + +func ExampleCrossJoinBy3() { + result := CrossJoinBy3([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, func(a string, b int, c bool) string { + return fmt.Sprintf("%v-%v-%v", a, b, c) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true + // a-1-false + // a-2-true + // a-2-false + // a-3-true + // a-3-false + // a-4-true + // a-4-false + // b-1-true + // b-1-false + // b-2-true + // b-2-false + // b-3-true + // b-3-false + // b-4-true + // b-4-false +} + +func ExampleCrossJoinBy4() { + result := CrossJoinBy4([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, func(a string, b int, c bool, d foo) string { + return fmt.Sprintf("%v-%v-%v-%v", a, b, c, d) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar} + // a-1-false-{bar} + // a-2-true-{bar} + // a-2-false-{bar} + // a-3-true-{bar} + // a-3-false-{bar} + // a-4-true-{bar} + // a-4-false-{bar} + // b-1-true-{bar} + // b-1-false-{bar} + // b-2-true-{bar} + // b-2-false-{bar} + // b-3-true-{bar} + // b-3-false-{bar} + // b-4-true-{bar} + // b-4-false-{bar} +} + +func ExampleCrossJoinBy5() { + result := CrossJoinBy5([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, func(a string, b int, c bool, d foo, e float64) string { + return fmt.Sprintf("%v-%v-%v-%v-%v", a, b, c, d, e) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar}-4.2 + // a-1-false-{bar}-4.2 + // a-2-true-{bar}-4.2 + // a-2-false-{bar}-4.2 + // a-3-true-{bar}-4.2 + // a-3-false-{bar}-4.2 + // a-4-true-{bar}-4.2 + // a-4-false-{bar}-4.2 + // b-1-true-{bar}-4.2 + // b-1-false-{bar}-4.2 + // b-2-true-{bar}-4.2 + // b-2-false-{bar}-4.2 + // b-3-true-{bar}-4.2 + // b-3-false-{bar}-4.2 + // b-4-true-{bar}-4.2 + // b-4-false-{bar}-4.2 +} + +func ExampleCrossJoinBy6() { + result := CrossJoinBy6([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, func(a string, b int, c bool, d foo, e float64, f string) string { + return fmt.Sprintf("%v-%v-%v-%v-%v-%v", a, b, c, d, e, f) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar}-4.2-plop + // a-1-false-{bar}-4.2-plop + // a-2-true-{bar}-4.2-plop + // a-2-false-{bar}-4.2-plop + // a-3-true-{bar}-4.2-plop + // a-3-false-{bar}-4.2-plop + // a-4-true-{bar}-4.2-plop + // a-4-false-{bar}-4.2-plop + // b-1-true-{bar}-4.2-plop + // b-1-false-{bar}-4.2-plop + // b-2-true-{bar}-4.2-plop + // b-2-false-{bar}-4.2-plop + // b-3-true-{bar}-4.2-plop + // b-3-false-{bar}-4.2-plop + // b-4-true-{bar}-4.2-plop + // b-4-false-{bar}-4.2-plop +} + +func ExampleCrossJoinBy7() { + result := CrossJoinBy7([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}, func(a string, b int, c bool, d foo, e float64, f string, g bool) string { + return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar}-4.2-plop-false + // a-1-false-{bar}-4.2-plop-false + // a-2-true-{bar}-4.2-plop-false + // a-2-false-{bar}-4.2-plop-false + // a-3-true-{bar}-4.2-plop-false + // a-3-false-{bar}-4.2-plop-false + // a-4-true-{bar}-4.2-plop-false + // a-4-false-{bar}-4.2-plop-false + // b-1-true-{bar}-4.2-plop-false + // b-1-false-{bar}-4.2-plop-false + // b-2-true-{bar}-4.2-plop-false + // b-2-false-{bar}-4.2-plop-false + // b-3-true-{bar}-4.2-plop-false + // b-3-false-{bar}-4.2-plop-false + // b-4-true-{bar}-4.2-plop-false + // b-4-false-{bar}-4.2-plop-false +} + +func ExampleCrossJoinBy8() { + result := CrossJoinBy8([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}, []int{42}, func(a string, b int, c bool, d foo, e float64, f string, g bool, h int) string { + return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g, h) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar}-4.2-plop-false-42 + // a-1-false-{bar}-4.2-plop-false-42 + // a-2-true-{bar}-4.2-plop-false-42 + // a-2-false-{bar}-4.2-plop-false-42 + // a-3-true-{bar}-4.2-plop-false-42 + // a-3-false-{bar}-4.2-plop-false-42 + // a-4-true-{bar}-4.2-plop-false-42 + // a-4-false-{bar}-4.2-plop-false-42 + // b-1-true-{bar}-4.2-plop-false-42 + // b-1-false-{bar}-4.2-plop-false-42 + // b-2-true-{bar}-4.2-plop-false-42 + // b-2-false-{bar}-4.2-plop-false-42 + // b-3-true-{bar}-4.2-plop-false-42 + // b-3-false-{bar}-4.2-plop-false-42 + // b-4-true-{bar}-4.2-plop-false-42 + // b-4-false-{bar}-4.2-plop-false-42 +} + +func ExampleCrossJoinBy9() { + result := CrossJoinBy9([]string{"a", "b"}, []int{1, 2, 3, 4}, []bool{true, false}, []foo{{bar: "bar"}}, []float64{4.2}, []string{"plop"}, []bool{false}, []int{42}, []string{"hello world"}, func(a string, b int, c bool, d foo, e float64, f string, g bool, h int, i string) string { + return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g, h, i) + }) + for _, r := range result { + fmt.Printf("%v\n", r) + } + // Output: + // a-1-true-{bar}-4.2-plop-false-42-hello world + // a-1-false-{bar}-4.2-plop-false-42-hello world + // a-2-true-{bar}-4.2-plop-false-42-hello world + // a-2-false-{bar}-4.2-plop-false-42-hello world + // a-3-true-{bar}-4.2-plop-false-42-hello world + // a-3-false-{bar}-4.2-plop-false-42-hello world + // a-4-true-{bar}-4.2-plop-false-42-hello world + // a-4-false-{bar}-4.2-plop-false-42-hello world + // b-1-true-{bar}-4.2-plop-false-42-hello world + // b-1-false-{bar}-4.2-plop-false-42-hello world + // b-2-true-{bar}-4.2-plop-false-42-hello world + // b-2-false-{bar}-4.2-plop-false-42-hello world + // b-3-true-{bar}-4.2-plop-false-42-hello world + // b-3-false-{bar}-4.2-plop-false-42-hello world + // b-4-true-{bar}-4.2-plop-false-42-hello world + // b-4-false-{bar}-4.2-plop-false-42-hello world +} diff --git a/tuples_test.go b/tuples_test.go index fccf88c..ba7a7cc 100644 --- a/tuples_test.go +++ b/tuples_test.go @@ -529,3 +529,65 @@ func TestUnzipBy(t *testing.T) { is.Equal(r1, []string{"aa", "bb"}) is.Equal(r2, []int{2, 4}) } + +func TestCrossJoin(t *testing.T) { + t.Parallel() + is := assert.New(t) + + listOne := []string{"a", "b", "c"} + listTwo := []int{1, 2, 3} + emptyList := []any{} + mixedList := []any{9.6, 4, "foobar"} + + results1 := CrossJoin2(emptyList, listTwo) + is.Len(results1, 0) + + results2 := CrossJoin2(listOne, emptyList) + is.Len(results2, 0) + + results3 := CrossJoin2(emptyList, emptyList) + is.Len(results3, 0) + + results4 := CrossJoin2([]string{"a"}, listTwo) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("a", 2), T2("a", 3)}, results4) + + results5 := CrossJoin2(listOne, []int{1}) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("b", 1), T2("c", 1)}, results5) + + results6 := CrossJoin2(listOne, listTwo) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("a", 2), T2("a", 3), T2("b", 1), T2("b", 2), T2("b", 3), T2("c", 1), T2("c", 2), T2("c", 3)}, results6) + + results7 := CrossJoin2(listOne, mixedList) + is.Equal([]Tuple2[string, any]{T2[string, any]("a", 9.6), T2[string, any]("a", 4), T2[string, any]("a", "foobar"), T2[string, any]("b", 9.6), T2[string, any]("b", 4), T2[string, any]("b", "foobar"), T2[string, any]("c", 9.6), T2[string, any]("c", 4), T2[string, any]("c", "foobar")}, results7) +} + +func TestCrossJoinBy(t *testing.T) { + t.Parallel() + is := assert.New(t) + + listOne := []string{"a", "b", "c"} + listTwo := []int{1, 2, 3} + emptyList := []any{} + mixedList := []any{9.6, 4, "foobar"} + + results1 := CrossJoinBy2(emptyList, listTwo, T2[any, int]) + is.Len(results1, 0) + + results2 := CrossJoinBy2(listOne, emptyList, T2[string, any]) + is.Len(results2, 0) + + results3 := CrossJoinBy2(emptyList, emptyList, T2[any, any]) + is.Len(results3, 0) + + results4 := CrossJoinBy2([]string{"a"}, listTwo, T2[string, int]) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("a", 2), T2("a", 3)}, results4) + + results5 := CrossJoinBy2(listOne, []int{1}, T2[string, int]) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("b", 1), T2("c", 1)}, results5) + + results6 := CrossJoinBy2(listOne, listTwo, T2[string, int]) + is.Equal([]Tuple2[string, int]{T2("a", 1), T2("a", 2), T2("a", 3), T2("b", 1), T2("b", 2), T2("b", 3), T2("c", 1), T2("c", 2), T2("c", 3)}, results6) + + results7 := CrossJoinBy2(listOne, mixedList, T2[string, any]) + is.Equal([]Tuple2[string, any]{T2[string, any]("a", 9.6), T2[string, any]("a", 4), T2[string, any]("a", "foobar"), T2[string, any]("b", 9.6), T2[string, any]("b", 4), T2[string, any]("b", "foobar"), T2[string, any]("c", 9.6), T2[string, any]("c", 4), T2[string, any]("c", "foobar")}, results7) +}