Repository: elliotchance/pie (go)

Issue:
Equals(), NotEquals(), GreaterThan(), etc
Simple comparisons in the form of a simplified Only/Select.

Pull request description:
Equals functions
close #39 

Could you look at [NotEquals](https://github.com/zhiburt/pie/blob/db970271a68e222b1d8728ae5043ce90f9d8dd8a/functions/not_equals.go#L7) function please. I consider that's the best way right now.

As for GreaterThan() function what must it do? Compare length of slices?

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/elliotchance/pie/151)
<!-- Reviewable:end -->


Patch:
diff --git a/README.md b/README.md
index e55c8be..9d34462 100644
--- a/README.md
+++ b/README.md
@@ -185,6 +185,7 @@ The letters in brackets indicate:
 | [`Diff`](#diff) (E)                     | ✓      | ✓      | ✓      |      | n²       | Diff returns the elements that needs to be added or removed from the first slice to have the same elements in the second slice.  |
 | [`DropTop`](#droptop)                   | ✓      | ✓      | ✓      |      | n        | DropTop will return the rest slice after dropping the top n elements if the slice has less elements then n that'll return empty slice if n < 0 it'll return empty slice.  |
 | [`Each`](#each)                         | ✓      | ✓      | ✓      |      | n        | Each is more condensed version of Transform that allows an action to happen on each elements and pass the original slice on.  |
+| [`Equals`](#equals) (E)                 | ✓      | ✓      | ✓      |      | n        | Equals compare elements from the start to the end,  |
 | [`Extend`](#extend)                     | ✓      | ✓      | ✓      |      | n        | Extend will return a new slice with the slices of elements appended to the end.  |
 | [`Filter`](#filter)                     | ✓      | ✓      | ✓      |      | n        | Filter will return a new slice containing only the elements that return true from the condition. The returned slice may contain zero elements (nil).  |
 | [`FilterNot`](#filternot)               | ✓      | ✓      | ✓      |      | n        | FilterNot works the same as Filter, with a negated condition. That is, it will return a new slice only containing the elements that returned false from the condition. The returned slice may contain zero elements (nil).  |
@@ -344,6 +345,18 @@ cars.Each(func (car *Car) {
 
 
 
+## Equals
+
+Equals compare elements from the start to the end,
+
+
+if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+if each slice == nil is considered that they're equal
+
+
+if element realizes Equals interface it uses that method, in other way uses default compare
+
+
 ## Extend
 
 Extend will return a new slice with the slices of elements appended to the
diff --git a/functions/equals.go b/functions/equals.go
new file mode 100644
index 0000000..143bb97
--- /dev/null
+++ b/functions/equals.go
@@ -0,0 +1,21 @@
+package functions
+
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss SliceType) Equals(rhs SliceType) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !ss[i].Equals(rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/functions/main.go b/functions/main.go
index 0d707ec..e650658 100644
--- a/functions/main.go
+++ b/functions/main.go
@@ -33,6 +33,7 @@ var Functions = []struct {
 	{"Diff", "diff.go", ForAll, "n²"},
 	{"DropTop", "drop_top.go", ForAll, "n"},
 	{"Each", "each.go", ForAll, "n"},
+	{"Equals", "equals.go", ForAll, "n"},
 	{"Extend", "extend.go", ForAll, "n"},
 	{"Filter", "filter.go", ForAll, "n"},
 	{"FilterNot", "filter_not.go", ForAll, "n"},
diff --git a/pie/carpointers_pie.go b/pie/carpointers_pie.go
index 79dc29b..fe4990f 100755
--- a/pie/carpointers_pie.go
+++ b/pie/carpointers_pie.go
@@ -158,6 +158,26 @@ func (ss carPointers) Each(fn func(*car)) carPointers {
 	return ss
 }
 
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss carPointers) Equals(rhs carPointers) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !ss[i].Equals(rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Extend will return a new slice with the slices of elements appended to the
 // end.
 //
diff --git a/pie/cars_pie.go b/pie/cars_pie.go
index 53f1673..5520471 100755
--- a/pie/cars_pie.go
+++ b/pie/cars_pie.go
@@ -158,6 +158,26 @@ func (ss cars) Each(fn func(car)) cars {
 	return ss
 }
 
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss cars) Equals(rhs cars) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !(ss[i] == rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Extend will return a new slice with the slices of elements appended to the
 // end.
 //
diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go
index 8a5a460..975b4b9 100755
--- a/pie/float64s_pie.go
+++ b/pie/float64s_pie.go
@@ -196,6 +196,26 @@ func (ss Float64s) Each(fn func(float64)) Float64s {
 	return ss
 }
 
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss Float64s) Equals(rhs Float64s) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !(ss[i] == rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Extend will return a new slice with the slices of elements appended to the
 // end.
 //
diff --git a/pie/ints_pie.go b/pie/ints_pie.go
index 34b0210..b7c67d7 100755
--- a/pie/ints_pie.go
+++ b/pie/ints_pie.go
@@ -196,6 +196,26 @@ func (ss Ints) Each(fn func(int)) Ints {
 	return ss
 }
 
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss Ints) Equals(rhs Ints) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !(ss[i] == rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Extend will return a new slice with the slices of elements appended to the
 // end.
 //
diff --git a/pie/strings_pie.go b/pie/strings_pie.go
index 141d401..e398516 100755
--- a/pie/strings_pie.go
+++ b/pie/strings_pie.go
@@ -172,6 +172,26 @@ func (ss Strings) Each(fn func(string)) Strings {
 	return ss
 }
 
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss Strings) Equals(rhs Strings) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !(ss[i] == rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
 // Extend will return a new slice with the slices of elements appended to the
 // end.
 //
diff --git a/template.go b/template.go
index b35fe46..a724d6e 100644
--- a/template.go
+++ b/template.go
@@ -215,6 +215,28 @@ func (ss SliceType) Each(fn func(ElementType)) SliceType {
 
 	return ss
 }
+`,
+	"Equals": `package functions
+
+// Equals compare elements from the start to the end,
+//
+// if they are the same is considered the slices are equal if all elements are the same is considered the slices are equal
+// if each slice == nil is considered that they're equal
+//
+// if element realizes Equals interface it uses that method, in other way uses default compare
+func (ss SliceType) Equals(rhs SliceType) bool {
+	if len(ss) != len(rhs) {
+		return false
+	}
+
+	for i := range ss {
+		if !ss[i].Equals(rhs[i]) {
+			return false
+		}
+	}
+
+	return true
+}
 `,
 	"Extend": `package functions
 


Test patch:
diff --git a/pie/carpointers_test.go b/pie/carpointers_test.go
index 9342649..4771347 100644
--- a/pie/carpointers_test.go
+++ b/pie/carpointers_test.go
@@ -1228,3 +1228,27 @@ func TestCarPointers_FindFirstUsing(t *testing.T) {
 		})
 	}
 }
+
+var carPointersEqualsTests = []struct {
+	ss       carPointers
+	rhs      carPointers
+	expected bool
+}{
+	{nil, nil, true},
+	{carPointers{}, carPointers{}, true},
+	{nil, carPointers{}, true},
+	{carPointers{{Name: "1"}, {Name: "2"}}, carPointers{{Name: "1"}, {Name: "2"}}, true},
+	{carPointers{{Name: "1"}, {Name: "2"}}, carPointers{{Name: "1"}, {Name: "3"}}, false},
+	{carPointers{{Name: "1"}, {Name: "2"}}, carPointers{{Name: "1"}}, false},
+	{carPointers{{Name: "2"}}, carPointers{{Name: "1"}}, false},
+	{carPointers{{Name: "2"}}, nil, false},
+}
+
+func TestCarPointers_Equals(t *testing.T) {
+	for _, test := range carPointersEqualsTests {
+		t.Run("", func(t *testing.T) {
+			defer assertImmutableCarPointers(t, &test.ss)()
+			assert.Equal(t, test.expected, test.ss.Equals(test.rhs))
+		})
+	}
+}
diff --git a/pie/cars_test.go b/pie/cars_test.go
index 317a680..b9f0a4a 100644
--- a/pie/cars_test.go
+++ b/pie/cars_test.go
@@ -1201,3 +1201,27 @@ func TestCars_FindFirstUsing(t *testing.T) {
 		})
 	}
 }
+
+var carsEqualsTests = []struct {
+	ss       cars
+	rhs      cars
+	expected bool
+}{
+	{nil, nil, true},
+	{cars{}, cars{}, true},
+	{nil, cars{}, true},
+	{cars{{Name: "1"}, {Name: "2"}}, cars{{Name: "1"}, {Name: "2"}}, true},
+	{cars{{Name: "1"}, {Name: "2"}}, cars{{Name: "1"}, {Name: "3"}}, false},
+	{cars{{Name: "1"}, {Name: "2"}}, cars{{Name: "1"}}, false},
+	{cars{{Name: "2"}}, cars{{Name: "1"}}, false},
+	{cars{{Name: "2"}}, nil, false},
+}
+
+func TestCars_Equals(t *testing.T) {
+	for _, test := range carsEqualsTests {
+		t.Run("", func(t *testing.T) {
+			defer assertImmutableCars(t, &test.ss)()
+			assert.Equal(t, test.expected, test.ss.Equals(test.rhs))
+		})
+	}
+}
diff --git a/pie/float64s_test.go b/pie/float64s_test.go
index 1528b6d..2c0d2f3 100644
--- a/pie/float64s_test.go
+++ b/pie/float64s_test.go
@@ -1374,3 +1374,27 @@ func TestFloat64_FindFirstUsing(t *testing.T) {
 		})
 	}
 }
+
+var float64sEqualsTests = []struct {
+	ss       Float64s
+	rhs      Float64s
+	expected bool
+}{
+	{nil, nil, true},
+	{Float64s{}, Float64s{}, true},
+	{nil, Float64s{}, true},
+	{Float64s{1.0, 2.0}, Float64s{1.0, 2.0}, true},
+	{Float64s{1.0, 2.0}, Float64s{1.0, 5.0}, false},
+	{Float64s{1.0, 2.0}, Float64s{1.0}, false},
+	{Float64s{1.0}, Float64s{2.0}, false},
+	{Float64s{1.0}, nil, false},
+}
+
+func TestFloat64s_Equals(t *testing.T) {
+	for _, test := range float64sEqualsTests {
+		t.Run("", func(t *testing.T) {
+			defer assertImmutableFloat64s(t, &test.ss)()
+			assert.Equal(t, test.expected, test.ss.Equals(test.rhs))
+		})
+	}
+}
diff --git a/pie/ints_test.go b/pie/ints_test.go
index be08553..809892d 100644
--- a/pie/ints_test.go
+++ b/pie/ints_test.go
@@ -1363,3 +1363,27 @@ func TestInts_FindFirstUsing(t *testing.T) {
 		})
 	}
 }
+
+var intsEqualsTests = []struct {
+	ss       Ints
+	rhs      Ints
+	expected bool
+}{
+	{nil, nil, true},
+	{Ints{}, Ints{}, true},
+	{nil, Ints{}, true},
+	{Ints{1, 2}, Ints{1, 2}, true},
+	{Ints{1, 2}, Ints{1, 5}, false},
+	{Ints{1, 2}, Ints{1}, false},
+	{Ints{1}, Ints{2}, false},
+	{Ints{1}, nil, false},
+}
+
+func TestInts_Equals(t *testing.T) {
+	for _, test := range intsEqualsTests {
+		t.Run("", func(t *testing.T) {
+			defer assertImmutableInts(t, &test.ss)()
+			assert.Equal(t, test.expected, test.ss.Equals(test.rhs))
+		})
+	}
+}
diff --git a/pie/strings_test.go b/pie/strings_test.go
index 4388bf2..a2782cc 100644
--- a/pie/strings_test.go
+++ b/pie/strings_test.go
@@ -1393,3 +1393,27 @@ func TestStrings_FindFirstUsing(t *testing.T) {
 		})
 	}
 }
+
+var stringsEqualsTests = []struct {
+	ss       Strings
+	rhs      Strings
+	expected bool
+}{
+	{nil, nil, true},
+	{Strings{}, Strings{}, true},
+	{nil, Strings{}, true},
+	{Strings{"a", "b"}, Strings{"a", "b"}, true},
+	{Strings{"a", "b"}, Strings{"a", "c"}, false},
+	{Strings{"a", "b"}, Strings{"a"}, false},
+	{Strings{"a"}, Strings{"b"}, false},
+	{Strings{"a"}, nil, false},
+}
+
+func TestStrings_Equals(t *testing.T) {
+	for _, test := range stringsEqualsTests {
+		t.Run("", func(t *testing.T) {
+			defer assertImmutableStrings(t, &test.ss)()
+			assert.Equal(t, test.expected, test.ss.Equals(test.rhs))
+		})
+	}
+}