Repository: elliotchance/pie
Pull request #193 (2023-06-07 17:03:47)

Add Zip function
With `Zip` and `ZipLongest` function we can do:
```go
a := []int{1, 2, 3, 4, 5}
b := []float32{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}

for _, p := range pie.Zip(a, b) {
    fmt.Println(p)
}
for _, p := range pie.ZipLongest(a, b) {
    fmt.Println(p)
}
// {1 1.1}
// {2 2.2}
// {3 3.3}
// {4 4.4}
// {5 5.5}
// {0 6.6} <- 0 is default value not in original slice
```

It is the same as `zip()` and `itertools.zip_longest` in Python

```Python
a = [1, 2, 3, 4, 5]
b = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]

for p in zip(a, b):
    print(p)
```

```Python
a = [1, 2, 3, 4, 5]
b = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]

for p in itertools.zip_longest(a, b, fillvalue=0):
    print(p)
```

chocolacula: Unfortunately I don't see a possibility to add `Zip` to `OfSlice` because we need two generic parameters.

Patch:
diff --git a/v2/zip.go b/v2/zip.go
new file mode 100644
index 0000000..da1bbd6
--- /dev/null
+++ b/v2/zip.go
@@ -0,0 +1,27 @@
+package pie
+
+// A pair struct containing two zipped values.
+type Zipped[T1, T2 any] struct {
+	A T1
+	B T2
+}
+
+// Zip will return a new slice containing pairs with elements from input slices.
+// If input slices have diffrent length, the output slice will be truncated to
+// the length of the smallest input slice.
+func Zip[T1, T2 any](ss1 []T1, ss2 []T2) []Zipped[T1, T2] {
+	var minLen int
+
+	if len(ss1) <= len(ss2) {
+		minLen = len(ss1)
+	} else {
+		minLen = len(ss2)
+	}
+
+	ss3 := []Zipped[T1, T2]{}
+	for i := 0; i < minLen; i++ {
+		ss3 = append(ss3, Zipped[T1, T2]{ss1[i], ss2[i]})
+	}
+
+	return ss3
+}
diff --git a/v2/zip_longest.go b/v2/zip_longest.go
new file mode 100644
index 0000000..8dc9004
--- /dev/null
+++ b/v2/zip_longest.go
@@ -0,0 +1,37 @@
+package pie
+
+// ZipLongest will return a new slice containing pairs with elements from input slices.
+// If input slices have diffrent length, missing elements will be padded with default values.
+func ZipLongest[T1, T2 any](ss1 []T1, ss2 []T2) []Zipped[T1, T2] {
+	var minLen, maxLen int
+	var small int8
+
+	if len(ss1) <= len(ss2) {
+		small = 1
+		minLen = len(ss1)
+		maxLen = len(ss2)
+	} else {
+		small = 2
+		minLen = len(ss2)
+		maxLen = len(ss1)
+	}
+
+	ss3 := []Zipped[T1, T2]{}
+	for i := 0; i < minLen; i++ {
+		ss3 = append(ss3, Zipped[T1, T2]{ss1[i], ss2[i]})
+	}
+
+	if small == 1 {
+		var t T1
+		for i := minLen; i < maxLen; i++ {
+			ss3 = append(ss3, Zipped[T1, T2]{t, ss2[i]})
+		}
+	} else {
+		var t T2
+		for i := minLen; i < maxLen; i++ {
+			ss3 = append(ss3, Zipped[T1, T2]{ss1[i], t})
+		}
+	}
+
+	return ss3
+}


Test patch:
diff --git a/v2/zip_longest_test.go b/v2/zip_longest_test.go
new file mode 100644
index 0000000..24c966b
--- /dev/null
+++ b/v2/zip_longest_test.go
@@ -0,0 +1,18 @@
+package pie_test
+
+import (
+	"github.com/elliotchance/pie/v2"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestZipLongest(t *testing.T) {
+	for _, test := range zipTests {
+
+		t.Run("", func(t *testing.T) {
+			c := pie.ZipLongest(test.ss1, test.ss2)
+
+			assert.Equal(t, c, test.expectedLong)
+		})
+	}
+}
diff --git a/v2/zip_test.go b/v2/zip_test.go
new file mode 100644
index 0000000..95d09f8
--- /dev/null
+++ b/v2/zip_test.go
@@ -0,0 +1,62 @@
+package pie_test
+
+import (
+	"github.com/elliotchance/pie/v2"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+var zipTests = []struct {
+	ss1           []int
+	ss2           []float32
+	expectedShort []pie.Zipped[int, float32]
+	expectedLong  []pie.Zipped[int, float32]
+}{
+	{
+		[]int{},
+		[]float32{},
+		[]pie.Zipped[int, float32]{},
+		[]pie.Zipped[int, float32]{},
+	},
+	{
+		[]int{1, 2, 3, 4, 5},
+		[]float32{},
+		[]pie.Zipped[int, float32]{},
+		[]pie.Zipped[int, float32]{{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}},
+	},
+	{
+		[]int{},
+		[]float32{1.0, 2.0, 3.0, 4.0, 5.0},
+		[]pie.Zipped[int, float32]{},
+		[]pie.Zipped[int, float32]{{0, 1.0}, {0, 2.0}, {0, 3.0}, {0, 4.0}, {0, 5.0}},
+	},
+	{
+		[]int{1, 2, 3, 4, 5},
+		[]float32{1.0, 2.0, 3.0, 4.0, 5.0},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}},
+	},
+	{
+		[]int{1, 2, 3, 4, 5, 6, 7, 8},
+		[]float32{1.0, 2.0, 3.0, 4.0, 5.0},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}, {6, 0}, {7, 0}, {8, 0}},
+	},
+	{
+		[]int{1, 2, 3},
+		[]float32{1.0, 2.0, 3.0, 4.0, 5.0},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}},
+		[]pie.Zipped[int, float32]{{1, 1.0}, {2, 2.0}, {3, 3.0}, {0, 4.0}, {0, 5.0}},
+	},
+}
+
+func TestZip(t *testing.T) {
+	for _, test := range zipTests {
+
+		t.Run("", func(t *testing.T) {
+			c := pie.Zip(test.ss1, test.ss2)
+
+			assert.Equal(t, c, test.expectedShort)
+		})
+	}
+}