Repository: elliotchance/pie
Pull request #55 (2019-04-26 04:30:28)

Added all() and any()


<!-- 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/55)
<!-- Reviewable:end -->



Patch:
diff --git a/README.md b/README.md
index 379a50e..2780631 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,8 @@ This will only generate `myInts.Average`, `myInts.Sum` and `myStrings.Select`.
 
 | Function     | String | Number | Struct | Big-O    | Description |
 | ------------ | :----: | :----: | :----: | :------: | ----------- |
+| `All`        | ✓      | ✓      | ✓      | n        | All will return true if all callbacks return true. If the list is empty then true is always returned. |
+| `Any`        | ✓      | ✓      | ✓      | n        | Any will return true if any callbacks return true. If the list is empty then false is always returned. |
 | `Append`     | ✓      | ✓      | ✓      | n        | A new slice with the elements appended to the end. |
 | `AreSorted`  | ✓      | ✓      |        | n        | Check if the slice is already sorted. |
 | `AreUnique`  | ✓      | ✓      |        | n        | Check if the slice contains only unique elements. |
@@ -164,12 +166,35 @@ empty slices. Apart from less possible panics, it makes it easier to chain.
 4. **Immutable.** Functions never modify inputs, unlike some built-ins such as
 `sort.Strings`.
 
-## Can I contribute?
+## How do I contribute a function?
 
-Absolutely. Pull requests are always welcome. Your PR must include:
+Pull requests are always welcome.
 
-1. Unit tests.
-2. Update the README to list the new functions.
+Here is a comprehensive list of steps to follow to add a new function:
+
+1. Create a new file in the `functions/` directory. The file should be named the
+same as the function. You must include documentation for your function.
+
+2. Update `functions/main.go` to register the new function by adding an entry to
+`Functions`. Make sure you choose the correct `For` value that is appropriate
+for your function.
+
+3. Run `go generate ./... && go install && go generate ./...`. The first
+`generate` is to create the pie templates, `install` will update your binary for
+the annotations and the second `generate` will use the newly created templates
+to update the generated code for the internal types. If you encounter errors
+with your code you can safely rerun the command above.
+
+4. If you chose `ForAll` or `ForStructs`, then you must add unit tests to
+`pie/carpointers_test.go` and `pie/cars_test.go`.
+
+5. If you chose `ForAll`, `ForNumbersAndStrings` or `ForNumbers`, then you must
+add unit tests to `pie/float64s_test.go` and `pie/ints_test.go`.
+
+6. If you chose `ForAll` or `ForStrings`, then you must add unit tests to
+`pie/strings_test.go`.
+
+7. Update the README to list the new functions.
 
 ## Why is the emoji a slice of pizza instead of a pie?
 
diff --git a/functions/all.go b/functions/all.go
new file mode 100644
index 0000000..78c8d98
--- /dev/null
+++ b/functions/all.go
@@ -0,0 +1,15 @@
+package functions
+
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss SliceType) All(fn func(value ElementType) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/functions/any.go b/functions/any.go
new file mode 100644
index 0000000..4a81ed6
--- /dev/null
+++ b/functions/any.go
@@ -0,0 +1,15 @@
+package functions
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss SliceType) Any(fn func(value ElementType) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/functions/main.go b/functions/main.go
index c2aedbb..1fdb18a 100644
--- a/functions/main.go
+++ b/functions/main.go
@@ -17,6 +17,8 @@ var Functions = []struct {
 	File string
 	For  int
 }{
+	{"All", "all.go", ForAll},
+	{"Any", "any.go", ForAll},
 	{"Append", "append.go", ForAll},
 	{"AreSorted", "are_sorted.go", ForNumbersAndStrings},
 	{"AreUnique", "are_unique.go", ForNumbersAndStrings},
diff --git a/pie/carpointers_pie.go b/pie/carpointers_pie.go
index 61dc2ee..682af6b 100755
--- a/pie/carpointers_pie.go
+++ b/pie/carpointers_pie.go
@@ -4,6 +4,34 @@ import (
 	"encoding/json"
 )
 
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss carPointers) All(fn func(value *car) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss carPointers) Any(fn func(value *car) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // Append will return a new slice with the elements appended to the end. It is a
 // wrapper for the internal append(). It is offered as a function so that it can
 // more easily chained.
diff --git a/pie/cars_pie.go b/pie/cars_pie.go
index b70b708..9ab580c 100755
--- a/pie/cars_pie.go
+++ b/pie/cars_pie.go
@@ -4,6 +4,34 @@ import (
 	"encoding/json"
 )
 
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss cars) All(fn func(value car) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss cars) Any(fn func(value car) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // Append will return a new slice with the elements appended to the end. It is a
 // wrapper for the internal append(). It is offered as a function so that it can
 // more easily chained.
diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go
index 1ff2abd..332f525 100755
--- a/pie/float64s_pie.go
+++ b/pie/float64s_pie.go
@@ -5,6 +5,34 @@ import (
 	"sort"
 )
 
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss Float64s) All(fn func(value float64) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss Float64s) Any(fn func(value float64) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // Append will return a new slice with the elements appended to the end. It is a
 // wrapper for the internal append(). It is offered as a function so that it can
 // more easily chained.
diff --git a/pie/ints_pie.go b/pie/ints_pie.go
index a915bd5..fdc0485 100755
--- a/pie/ints_pie.go
+++ b/pie/ints_pie.go
@@ -5,6 +5,34 @@ import (
 	"sort"
 )
 
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss Ints) All(fn func(value int) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss Ints) Any(fn func(value int) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // Append will return a new slice with the elements appended to the end. It is a
 // wrapper for the internal append(). It is offered as a function so that it can
 // more easily chained.
diff --git a/pie/strings_pie.go b/pie/strings_pie.go
index e0e404a..2cf14e8 100755
--- a/pie/strings_pie.go
+++ b/pie/strings_pie.go
@@ -5,6 +5,34 @@ import (
 	"sort"
 )
 
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss Strings) All(fn func(value string) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss Strings) Any(fn func(value string) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // Append will return a new slice with the elements appended to the end. It is a
 // wrapper for the internal append(). It is offered as a function so that it can
 // more easily chained.
diff --git a/template.go b/template.go
index df5d27a..1c39a45 100644
--- a/template.go
+++ b/template.go
@@ -2,6 +2,38 @@
 package main
 
 var pieTemplates = map[string]string{
+	"All": `package functions
+
+// All will return true if all callbacks return true. It follows the same logic
+// as the all() function in Python.
+//
+// If the list is empty then true is always returned.
+func (ss SliceType) All(fn func(value ElementType) bool) bool {
+	for _, value := range ss {
+		if !fn(value) {
+			return false
+		}
+	}
+
+	return true
+}
+`,
+	"Any": `package functions
+
+// Any will return true if any callbacks return true. It follows the same logic
+// as the any() function in Python.
+//
+// If the list is empty then false is always returned.
+func (ss SliceType) Any(fn func(value ElementType) bool) bool {
+	for _, value := range ss {
+		if fn(value) {
+			return true
+		}
+	}
+
+	return false
+}
+`,
 	"Append": `package functions
 
 // Append will return a new slice with the elements appended to the end. It is a


Test patch:
diff --git a/pie/carpointers_test.go b/pie/carpointers_test.go
index 528ce0f..6b2c99d 100644
--- a/pie/carpointers_test.go
+++ b/pie/carpointers_test.go
@@ -385,3 +385,73 @@ func TestCarPointers_Extend(t *testing.T) {
 		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}, &car{"foo", "red"}},
 	)
 }
+
+func TestCarPointers_All(t *testing.T) {
+	assert.True(t,
+		(carPointers)(nil).All(func(value *car) bool {
+			return false
+		}),
+	)
+
+	assert.True(t,
+		(carPointers)(nil).All(func(value *car) bool {
+			return false
+		}),
+	)
+
+	// None
+	assert.False(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.All(func(value *car) bool {
+			return value.Color == "green"
+		}),
+	)
+
+	// Some
+	assert.False(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.All(func(value *car) bool {
+			return value.Color == "yellow"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.All(func(value *car) bool {
+			return len(value.Name) > 0
+		}),
+	)
+}
+
+func TestCarPointers_Any(t *testing.T) {
+	assert.False(t,
+		(carPointers)(nil).Any(func(value *car) bool {
+			return true
+		}),
+	)
+
+	assert.False(t,
+		(carPointers)(nil).Any(func(value *car) bool {
+			return true
+		}),
+	)
+
+	// None
+	assert.False(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.Any(func(value *car) bool {
+			return value.Color == "green"
+		}),
+	)
+
+	// Some
+	assert.True(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.Any(func(value *car) bool {
+			return value.Color == "yellow"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}}.Any(func(value *car) bool {
+			return len(value.Name) > 0
+		}),
+	)
+}
diff --git a/pie/cars_test.go b/pie/cars_test.go
index 203e212..066342f 100644
--- a/pie/cars_test.go
+++ b/pie/cars_test.go
@@ -375,3 +375,73 @@ func TestCars_Extend(t *testing.T) {
 		cars{car{"bar", "yellow"}, car{"Baz", "black"}, car{"foo", "red"}},
 	)
 }
+
+func TestCars_All(t *testing.T) {
+	assert.True(t,
+		(cars)(nil).All(func(value car) bool {
+			return false
+		}),
+	)
+
+	assert.True(t,
+		(cars)(nil).All(func(value car) bool {
+			return false
+		}),
+	)
+
+	// None
+	assert.False(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.All(func(value car) bool {
+			return value.Color == "green"
+		}),
+	)
+
+	// Some
+	assert.False(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.All(func(value car) bool {
+			return value.Color == "yellow"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.All(func(value car) bool {
+			return len(value.Name) > 0
+		}),
+	)
+}
+
+func TestCars_Any(t *testing.T) {
+	assert.False(t,
+		(cars)(nil).Any(func(value car) bool {
+			return true
+		}),
+	)
+
+	assert.False(t,
+		(cars)(nil).Any(func(value car) bool {
+			return true
+		}),
+	)
+
+	// None
+	assert.False(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.Any(func(value car) bool {
+			return value.Color == "green"
+		}),
+	)
+
+	// Some
+	assert.True(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.Any(func(value car) bool {
+			return value.Color == "yellow"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		cars{car{"bar", "yellow"}, car{"Baz", "black"}}.Any(func(value car) bool {
+			return len(value.Name) > 0
+		}),
+	)
+}
diff --git a/pie/float64s_test.go b/pie/float64s_test.go
index d80562e..525f9e8 100644
--- a/pie/float64s_test.go
+++ b/pie/float64s_test.go
@@ -487,3 +487,73 @@ func TestFloat64s_Extend(t *testing.T) {
 		Float64s{1.23, 2.34, 5.67},
 	)
 }
+
+func TestFloat64s_All(t *testing.T) {
+	assert.True(t,
+		Float64s{}.All(func(value float64) bool {
+			return false
+		}),
+	)
+
+	assert.True(t,
+		Float64s{}.All(func(value float64) bool {
+			return false
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Float64s{12.3, 4.56}.All(func(value float64) bool {
+			return value == 1
+		}),
+	)
+
+	// Some
+	assert.False(t,
+		Float64s{12.3, 4.56}.All(func(value float64) bool {
+			return value == 12.3
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Float64s{12.3, 4.56}.All(func(value float64) bool {
+			return value > 0
+		}),
+	)
+}
+
+func TestFloat64s_Any(t *testing.T) {
+	assert.False(t,
+		Float64s{}.Any(func(value float64) bool {
+			return true
+		}),
+	)
+
+	assert.False(t,
+		Float64s{}.Any(func(value float64) bool {
+			return true
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Float64s{12.3, 4.56}.Any(func(value float64) bool {
+			return value == 1
+		}),
+	)
+
+	// Some
+	assert.True(t,
+		Float64s{12.3, 4.56}.Any(func(value float64) bool {
+			return value == 12.3
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Float64s{12.3, 4.56}.Any(func(value float64) bool {
+			return value > 0
+		}),
+	)
+}
diff --git a/pie/ints_test.go b/pie/ints_test.go
index 6e9be69..a4df88f 100644
--- a/pie/ints_test.go
+++ b/pie/ints_test.go
@@ -473,3 +473,73 @@ func TestInts_Extend(t *testing.T) {
 		Ints{1, 2, 5},
 	)
 }
+
+func TestInts_All(t *testing.T) {
+	assert.True(t,
+		Ints{}.All(func(value int) bool {
+			return false
+		}),
+	)
+
+	assert.True(t,
+		Ints{}.All(func(value int) bool {
+			return false
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Ints{12, 4}.All(func(value int) bool {
+			return value == 1
+		}),
+	)
+
+	// Some
+	assert.False(t,
+		Ints{12, 4}.All(func(value int) bool {
+			return value == 12
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Ints{12, 4}.All(func(value int) bool {
+			return value > 0
+		}),
+	)
+}
+
+func TestInts_Any(t *testing.T) {
+	assert.False(t,
+		Ints{}.Any(func(value int) bool {
+			return true
+		}),
+	)
+
+	assert.False(t,
+		Ints{}.Any(func(value int) bool {
+			return true
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Ints{12, 4}.Any(func(value int) bool {
+			return value == 1
+		}),
+	)
+
+	// Some
+	assert.True(t,
+		Ints{12, 4}.Any(func(value int) bool {
+			return value == 12
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Ints{12, 4}.Any(func(value int) bool {
+			return value > 0
+		}),
+	)
+}
diff --git a/pie/strings_test.go b/pie/strings_test.go
index 297f2fe..40fea5e 100644
--- a/pie/strings_test.go
+++ b/pie/strings_test.go
@@ -466,3 +466,73 @@ func TestStrings_Extend(t *testing.T) {
 		Strings{"bar", "Baz", "foo"},
 	)
 }
+
+func TestStrings_All(t *testing.T) {
+	assert.True(t,
+		Strings{}.All(func(value string) bool {
+			return false
+		}),
+	)
+
+	assert.True(t,
+		Strings{}.All(func(value string) bool {
+			return false
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Strings{"foo", "bar"}.All(func(value string) bool {
+			return value == "baz"
+		}),
+	)
+
+	// Some
+	assert.False(t,
+		Strings{"foo", "bar"}.All(func(value string) bool {
+			return value == "foo"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Strings{"foo", "bar"}.All(func(value string) bool {
+			return len(value) > 0
+		}),
+	)
+}
+
+func TestStrings_Any(t *testing.T) {
+	assert.False(t,
+		Strings{}.Any(func(value string) bool {
+			return true
+		}),
+	)
+
+	assert.False(t,
+		Strings{}.Any(func(value string) bool {
+			return true
+		}),
+	)
+
+	// None
+	assert.False(t,
+		Strings{"foo", "bar"}.Any(func(value string) bool {
+			return value == "baz"
+		}),
+	)
+
+	// Some
+	assert.True(t,
+		Strings{"foo", "bar"}.Any(func(value string) bool {
+			return value == "foo"
+		}),
+	)
+
+	// All
+	assert.True(t,
+		Strings{"foo", "bar"}.Any(func(value string) bool {
+			return len(value) > 0
+		}),
+	)
+}