Repository: elliotchance/pie
Pull request #102 (2019-05-05 13:33:06)

Unique is O(n).
See #92

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


codecov-io: # [Codecov](https://codecov.io/gh/elliotchance/pie/pull/102?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@57366d4`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/elliotchance/pie/pull/102/graphs/tree.svg?width=650&token=OvugwUQGM3&height=150&src=pr)](https://codecov.io/gh/elliotchance/pie/pull/102?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##             master   #102   +/-   ##
=======================================
  Coverage          ?   100%           
=======================================
  Files             ?      7           
  Lines             ?    783           
  Branches          ?      0           
=======================================
  Hits              ?    783           
  Misses            ?      0           
  Partials          ?      0
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/elliotchance/pie/pull/102?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/elliotchance/pie/pull/102?src=pr&el=footer). Last update [57366d4...0fc2a4e](https://codecov.io/gh/elliotchance/pie/pull/102?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.md b/README.md
index 354dfb8..f223efa 100644
--- a/README.md
+++ b/README.md
@@ -152,14 +152,15 @@ This will only generate `myInts.Average`, `myInts.Sum` and `myStrings.Filter`.
 | `Min`        | ✓      | ✓      |       |      | n        | The minimum value, or a zeroed value. |
 | `Product`    |        | ✓      |       |      | n        | Product of all elements. |
 | `Random`     | ✓      | ✓      | ✓     |      | 1        | Select a random element, or a zeroed value if empty. |
+| `Reduce`     | ✓      | ✓      |       |      | n        | Continously apply a function to the slice (left to right), reducing it to a single value. |
 | `Reverse`    | ✓      | ✓      | ✓     |      | n        | Reverse elements. |
-| `Send`     | ✓      | ✓      | ✓     |      | n        | Send all element to channel. |
+| `Send`       | ✓      | ✓      | ✓     |      | n        | Send all element to channel. |
 | `Sort`       | ✓      | ✓      |       |      | n⋅log(n) | Return a new sorted slice. |
 | `Sum`        |        | ✓      |       |      | n        | Sum (total) of all elements. |
 | `Shuffle`    | ✓      | ✓      | ✓     |      | n        | Returns a new shuffled slice. |
 | `Top`        | ✓      | ✓      | ✓     |      | n        | Gets several elements from top(head of slice).|
 | `ToStrings`  | ✓      | ✓      | ✓     |      | n        | Transforms each element to a string. |
-| `Unique`     | ✓      | ✓      |       |      | n⋅log(n) | Return a new slice with only unique elements. |
+| `Unique`     | ✓      | ✓      |       |      | n        | Return a new slice with only unique elements. |
 | `Values`     |        |        |       | ✓    | n        | Returns all values in the map (in random order). |
 
 # FAQ
diff --git a/functions/main.go b/functions/main.go
index 1099436..50feb2e 100644
--- a/functions/main.go
+++ b/functions/main.go
@@ -46,6 +46,7 @@ var Functions = []struct {
 	{"Min", "min.go", ForNumbersAndStrings},
 	{"Product", "product.go", ForNumbers},
 	{"Random", "random.go", ForAll},
+	{"Reduce", "reduce.go", ForNumbersAndStrings},
 	{"Reverse", "reverse.go", ForAll},
 	{"Send", "send.go", ForAll},
 	{"Sort", "sort.go", ForNumbersAndStrings},
diff --git a/functions/reduce.go b/functions/reduce.go
new file mode 100644
index 0000000..7fa3a8e
--- /dev/null
+++ b/functions/reduce.go
@@ -0,0 +1,17 @@
+package functions
+
+// Reduce continually applies the provided function
+// over the slice. Reducing the elements to a single value.
+//
+// Returns a zero value of ElementType if there are no elements in the slice. It will panic if the reducer is nil and the slice has more than one element (required to invoke reduce).
+// Otherwise returns result of applying reducer from left to right.
+func (ss SliceType) Reduce(reducer func(ElementType, ElementType) ElementType) (el ElementType) {
+	if len(ss) == 0 {
+		return
+	}
+	el = ss[0]
+	for _, s := range ss[1:] {
+		el = reducer(el, s)
+	}
+	return
+}
diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go
index cb286e9..e6cfa60 100755
--- a/pie/float64s_pie.go
+++ b/pie/float64s_pie.go
@@ -359,6 +359,22 @@ func (ss Float64s) Random(source rand.Source) float64 {
 	return ss[i]
 }
 
+// Reduce continually applies the provided function
+// over the slice. Reducing the elements to a single value.
+//
+// Returns a zero value of float64 if there are no elements in the slice. It will panic if the reducer is nil and the slice has more than one element (required to invoke reduce).
+// Otherwise returns result of applying reducer from left to right.
+func (ss Float64s) Reduce(reducer func(float64, float64) float64) (el float64) {
+	if len(ss) == 0 {
+		return
+	}
+	el = ss[0]
+	for _, s := range ss[1:] {
+		el = reducer(el, s)
+	}
+	return
+}
+
 // Reverse returns a new copy of the slice with the elements ordered in reverse.
 // This is useful when combined with Sort to get a descending sort order:
 //
diff --git a/pie/ints_pie.go b/pie/ints_pie.go
index e8fa45d..4542326 100755
--- a/pie/ints_pie.go
+++ b/pie/ints_pie.go
@@ -359,6 +359,22 @@ func (ss Ints) Random(source rand.Source) int {
 	return ss[i]
 }
 
+// Reduce continually applies the provided function
+// over the slice. Reducing the elements to a single value.
+//
+// Returns a zero value of int if there are no elements in the slice. It will panic if the reducer is nil and the slice has more than one element (required to invoke reduce).
+// Otherwise returns result of applying reducer from left to right.
+func (ss Ints) Reduce(reducer func(int, int) int) (el int) {
+	if len(ss) == 0 {
+		return
+	}
+	el = ss[0]
+	for _, s := range ss[1:] {
+		el = reducer(el, s)
+	}
+	return
+}
+
 // Reverse returns a new copy of the slice with the elements ordered in reverse.
 // This is useful when combined with Sort to get a descending sort order:
 //
diff --git a/pie/strings_pie.go b/pie/strings_pie.go
index 0dd7452..928648b 100755
--- a/pie/strings_pie.go
+++ b/pie/strings_pie.go
@@ -315,6 +315,22 @@ func (ss Strings) Random(source rand.Source) string {
 	return ss[i]
 }
 
+// Reduce continually applies the provided function
+// over the slice. Reducing the elements to a single value.
+//
+// Returns a zero value of string if there are no elements in the slice. It will panic if the reducer is nil and the slice has more than one element (required to invoke reduce).
+// Otherwise returns result of applying reducer from left to right.
+func (ss Strings) Reduce(reducer func(string, string) string) (el string) {
+	if len(ss) == 0 {
+		return
+	}
+	el = ss[0]
+	for _, s := range ss[1:] {
+		el = reducer(el, s)
+	}
+	return
+}
+
 // Reverse returns a new copy of the slice with the elements ordered in reverse.
 // This is useful when combined with Sort to get a descending sort order:
 //
diff --git a/template.go b/template.go
index b282e93..2c5a0a1 100644
--- a/template.go
+++ b/template.go
@@ -456,6 +456,24 @@ func (ss SliceType) Random(source rand.Source) ElementType {
 	i := rnd.Intn(n)
 	return ss[i]
 }
+`,
+	"Reduce": `package functions
+
+// Reduce continually applies the provided function
+// over the slice. Reducing the elements to a single value.
+//
+// Returns a zero value of ElementType if there are no elements in the slice. It will panic if the reducer is nil and the slice has more than one element (required to invoke reduce).
+// Otherwise returns result of applying reducer from left to right.
+func (ss SliceType) Reduce(reducer func(ElementType, ElementType) ElementType) (el ElementType) {
+	if len(ss) == 0 {
+		return
+	}
+	el = ss[0]
+	for _, s := range ss[1:] {
+		el = reducer(el, s)
+	}
+	return
+}
 `,
 	"Reverse": `package functions
 


Test patch:
diff --git a/pie/float64s_test.go b/pie/float64s_test.go
index bcb0902..08613a6 100644
--- a/pie/float64s_test.go
+++ b/pie/float64s_test.go
@@ -746,6 +746,41 @@ func TestFloat64s_Random(t *testing.T) {
 	}
 }
 
+var float64sReduceTests = []struct {
+	ss       Float64s
+	expected float64
+	reducer  func(a, b float64) float64
+}{
+	{
+		Float64s{1, 2, 3},
+		6,
+		func(a, b float64) float64 { return a + b },
+	},
+	{
+		Float64s{1, 2, 3},
+		-4,
+		func(a, b float64) float64 { return a - b },
+	},
+	{
+		Float64s{},
+		0,
+		func(a, b float64) float64 { return a - b },
+	},
+	{
+		Float64s{1},
+		1,
+		func(a, b float64) float64 { return a - b },
+	},
+}
+
+func TestFloat64s_Reduce(t *testing.T) {
+	for _, test := range float64sReduceTests {
+		t.Run("", func(t *testing.T) {
+			assert.Equal(t, test.expected, test.ss.Reduce(test.reducer))
+		})
+	}
+}
+
 var float64sAbsTests = []struct {
 	ss  Float64s
 	abs Float64s
diff --git a/pie/ints_test.go b/pie/ints_test.go
index 686913b..dcc1874 100644
--- a/pie/ints_test.go
+++ b/pie/ints_test.go
@@ -731,6 +731,41 @@ func TestInts_Random(t *testing.T) {
 	}
 }
 
+var intsReduceTests = []struct {
+	ss       Ints
+	expected int
+	reducer  func(a, b int) int
+}{
+	{
+		Ints{1, 2, 3},
+		6,
+		func(a, b int) int { return a + b },
+	},
+	{
+		Ints{1, 2, 3},
+		-4,
+		func(a, b int) int { return a - b },
+	},
+	{
+		Ints{},
+		0,
+		func(a, b int) int { return a - b },
+	},
+	{
+		Ints{1},
+		1,
+		func(a, b int) int { return a - b },
+	},
+}
+
+func TestInts_Reduce(t *testing.T) {
+	for _, test := range intsReduceTests {
+		t.Run("", func(t *testing.T) {
+			assert.Equal(t, test.expected, test.ss.Reduce(test.reducer))
+		})
+	}
+}
+
 func TestInts_Abs(t *testing.T) {
 	assert.Equal(t, Ints{1, 5, 7}, Ints{-1, 5, -7}.Abs())
 	assert.Equal(t, Ints{689845, 688969, 220373, 89437, 308836}, Ints{-689845, -688969, -220373, -89437, 308836}.Abs())
diff --git a/pie/strings_test.go b/pie/strings_test.go
index 5fef94a..909ba21 100644
--- a/pie/strings_test.go
+++ b/pie/strings_test.go
@@ -705,6 +705,36 @@ func TestStrings_Random(t *testing.T) {
 	}
 }
 
+var stringsReduceTests = []struct {
+	ss       Strings
+	expected string
+	reducer  func(a, b string) string
+}{
+	{
+		Strings{"Hello", " ", "World"},
+		"Hello World",
+		func(a, b string) string { return a + b },
+	},
+	{
+		Strings{},
+		"",
+		func(a, b string) string { return a + b },
+	},
+	{
+		Strings{"Hello"},
+		"Hello",
+		func(a, b string) string { return a + b },
+	},
+}
+
+func TestStrings_Reduce(t *testing.T) {
+	for _, test := range stringsReduceTests {
+		t.Run("", func(t *testing.T) {
+			assert.Equal(t, test.expected, test.ss.Reduce(test.reducer))
+		})
+	}
+}
+
 var stringsSendTests = []struct {
 	ss            Strings
 	recieveDelay  time.Duration