From 7ff9a2143d7c4b1cd63e4f90dfe53b958fc729e8 Mon Sep 17 00:00:00 2001 From: Zhiburt Date: Tue, 7 May 2019 02:15:56 +0300 Subject: [PATCH] Intersect (#94) Intersect returns items that exist in all lists. It returns slice without any duplicates. If zero slice arguments are provided, then nil is returned. Fixes #71 Updates #72 --- README.md | 1 + functions/intersect.go | 36 +++++++++++++++++++++++++++++++++ functions/main.go | 1 + pie/float64s_pie.go | 35 ++++++++++++++++++++++++++++++++ pie/float64s_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ pie/ints_pie.go | 35 ++++++++++++++++++++++++++++++++ pie/ints_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ pie/strings_pie.go | 35 ++++++++++++++++++++++++++++++++ pie/strings_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ template.go | 37 +++++++++++++++++++++++++++++++++ 10 files changed, 318 insertions(+) create mode 100644 functions/intersect.go diff --git a/README.md b/README.md index e76dcbe..aef4656 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ This will only generate `myInts.Average`, `myInts.Sum` and `myStrings.Filter`. | `FilterNot` | ✓ | ✓ | ✓ | | n | A new slice containing only the elements that returned false from the condition. | | `First` | ✓ | ✓ | ✓ | | 1 | The first element, or a zeroed value. | | `FirstOr` | ✓ | ✓ | ✓ | | 1 | The first element, or a default value. | +| `Intersect` | ✓ | ✓ | | | n | Intersect returns elements which exists in all slices. | | `Join` | ✓ | | | | n | A string from joining each of the elements. | | `JSONString` | ✓ | ✓ | ✓ | | n | The JSON encoded string. | | `Keys` | | | | ✓ | n | Returns all keys in the map (in random order). | diff --git a/functions/intersect.go b/functions/intersect.go new file mode 100644 index 0000000..0e5ac17 --- /dev/null +++ b/functions/intersect.go @@ -0,0 +1,36 @@ +package functions + +// Intersect returns items that exist in all lists. +// +// It returns slice without any duplicates. +// If zero slice arguments are provided, then nil is returned. +func (ss SliceType) Intersect(slices ...SliceType) (ss2 SliceType) { + if slices == nil { + return nil + } + + var uniqs = make([]map[ElementType]struct{}, len(slices)) + for i := 0; i < len(slices); i++ { + m := make(map[ElementType]struct{}) + for _, el := range slices[i] { + m[el] = struct{}{} + } + uniqs[i] = m + } + + var containsInAll = false + for _, el := range ss.Unique() { + for _, u := range uniqs { + if _, exists := u[el]; !exists { + containsInAll = false + break + } + containsInAll = true + } + if containsInAll { + ss2 = append(ss2, el) + } + } + + return +} diff --git a/functions/main.go b/functions/main.go index c9ed4bf..a7618e7 100644 --- a/functions/main.go +++ b/functions/main.go @@ -33,6 +33,7 @@ var Functions = []struct { {"FilterNot", "filter_not.go", ForAll}, {"First", "first.go", ForAll}, {"FirstOr", "first_or.go", ForAll}, + {"Intersect", "intersect.go", ForNumbersAndStrings}, {"Join", "join.go", ForStrings}, {"JSONString", "json_string.go", ForAll}, {"Keys", "keys.go", ForMaps}, diff --git a/pie/float64s_pie.go b/pie/float64s_pie.go index 1628567..4621adc 100755 --- a/pie/float64s_pie.go +++ b/pie/float64s_pie.go @@ -186,6 +186,41 @@ func (ss Float64s) FirstOr(defaultValue float64) float64 { return ss[0] } +// Intersect returns items that exist in all lists. +// +// It returns slice without any duplicates. +// If zero slice arguments are provided, then nil is returned. +func (ss Float64s) Intersect(slices ...Float64s) (ss2 Float64s) { + if slices == nil { + return nil + } + + var uniqs = make([]map[float64]struct{}, len(slices)) + for i := 0; i < len(slices); i++ { + m := make(map[float64]struct{}) + for _, el := range slices[i] { + m[el] = struct{}{} + } + uniqs[i] = m + } + + var containsInAll = false + for _, el := range ss.Unique() { + for _, u := range uniqs { + if _, exists := u[el]; !exists { + containsInAll = false + break + } + containsInAll = true + } + if containsInAll { + ss2 = append(ss2, el) + } + } + + return +} + // JSONString returns the JSON encoded array as a string. // // One important thing to note is that it will treat a nil slice as an empty diff --git a/pie/float64s_test.go b/pie/float64s_test.go index ea49565..d334b9b 100644 --- a/pie/float64s_test.go +++ b/pie/float64s_test.go @@ -807,3 +807,49 @@ func TestFloat64s_Send(t *testing.T) { }) } } + +var float64sIntersectTests = []struct { + ss Float64s + params []Float64s + expected Float64s +}{ + { + nil, + nil, + nil, + }, + { + Float64s{1.2, 3.2}, + nil, + nil, + }, + { + nil, + []Float64s{{1.2, 3.2, 5.5}, {5.5, 1.2}}, + nil, + }, + { + Float64s{1.2, 3.2}, + []Float64s{{1.2}, {3.2}}, + nil, + }, + { + Float64s{1.2, 3.2}, + []Float64s{{1.2}}, + Float64s{1.2}, + }, + { + Float64s{1.2, 3.2}, + []Float64s{{1.2, 3.2, 5.5}, {5.5, 1.2}}, + Float64s{1.2}, + }, +} + +func TestFloat64s_Intersect(t *testing.T) { + for _, test := range float64sIntersectTests { + t.Run("", func(t *testing.T) { + defer assertImmutableFloat64s(t, &test.ss)() + assert.Equal(t, test.expected, test.ss.Intersect(test.params...)) + }) + } +} diff --git a/pie/ints_pie.go b/pie/ints_pie.go index 79cce7b..b1bb76f 100755 --- a/pie/ints_pie.go +++ b/pie/ints_pie.go @@ -186,6 +186,41 @@ func (ss Ints) FirstOr(defaultValue int) int { return ss[0] } +// Intersect returns items that exist in all lists. +// +// It returns slice without any duplicates. +// If zero slice arguments are provided, then nil is returned. +func (ss Ints) Intersect(slices ...Ints) (ss2 Ints) { + if slices == nil { + return nil + } + + var uniqs = make([]map[int]struct{}, len(slices)) + for i := 0; i < len(slices); i++ { + m := make(map[int]struct{}) + for _, el := range slices[i] { + m[el] = struct{}{} + } + uniqs[i] = m + } + + var containsInAll = false + for _, el := range ss.Unique() { + for _, u := range uniqs { + if _, exists := u[el]; !exists { + containsInAll = false + break + } + containsInAll = true + } + if containsInAll { + ss2 = append(ss2, el) + } + } + + return +} + // JSONString returns the JSON encoded array as a string. // // One important thing to note is that it will treat a nil slice as an empty diff --git a/pie/ints_test.go b/pie/ints_test.go index 42cba48..ade3d52 100644 --- a/pie/ints_test.go +++ b/pie/ints_test.go @@ -773,3 +773,49 @@ func TestInts_Send(t *testing.T) { }) } } + +var intsIntersectTests = []struct { + ss Ints + params []Ints + expected Ints +}{ + { + nil, + nil, + nil, + }, + { + Ints{1, 3}, + nil, + nil, + }, + { + nil, + []Ints{{1, 3, 5}, {5, 1}}, + nil, + }, + { + Ints{1, 3}, + []Ints{{1}, {3}}, + nil, + }, + { + Ints{1, 3}, + []Ints{{1}}, + Ints{1}, + }, + { + Ints{1, 3}, + []Ints{{1, 3, 5}, {5, 1}}, + Ints{1}, + }, +} + +func TestInts_Intersect(t *testing.T) { + for _, test := range intsIntersectTests { + t.Run("", func(t *testing.T) { + defer assertImmutableInts(t, &test.ss)() + assert.Equal(t, test.expected, test.ss.Intersect(test.params...)) + }) + } +} diff --git a/pie/strings_pie.go b/pie/strings_pie.go index 9c28f3f..0dd7452 100755 --- a/pie/strings_pie.go +++ b/pie/strings_pie.go @@ -166,6 +166,41 @@ func (ss Strings) FirstOr(defaultValue string) string { return ss[0] } +// Intersect returns items that exist in all lists. +// +// It returns slice without any duplicates. +// If zero slice arguments are provided, then nil is returned. +func (ss Strings) Intersect(slices ...Strings) (ss2 Strings) { + if slices == nil { + return nil + } + + var uniqs = make([]map[string]struct{}, len(slices)) + for i := 0; i < len(slices); i++ { + m := make(map[string]struct{}) + for _, el := range slices[i] { + m[el] = struct{}{} + } + uniqs[i] = m + } + + var containsInAll = false + for _, el := range ss.Unique() { + for _, u := range uniqs { + if _, exists := u[el]; !exists { + containsInAll = false + break + } + containsInAll = true + } + if containsInAll { + ss2 = append(ss2, el) + } + } + + return +} + // Join returns a string from joining each of the elements. func (ss Strings) Join(glue string) (s string) { for i, element := range ss { diff --git a/pie/strings_test.go b/pie/strings_test.go index c36f7a1..5fef94a 100644 --- a/pie/strings_test.go +++ b/pie/strings_test.go @@ -753,3 +753,49 @@ func TestStrings_Send(t *testing.T) { }) } } + +var stringsIntersectTests = []struct { + ss Strings + params []Strings + expected Strings +}{ + { + nil, + nil, + nil, + }, + { + Strings{"foo", "bar"}, + nil, + nil, + }, + { + nil, + []Strings{{"foo", "bar", "baz"}, {"baz", "foo"}}, + nil, + }, + { + Strings{"foo", "bar"}, + []Strings{{"bar"}, {"foo"}}, + nil, + }, + { + Strings{"foo", "bar"}, + []Strings{{"bar"}}, + Strings{"bar"}, + }, + { + Strings{"foo", "bar"}, + []Strings{{"foo", "bar", "baz"}, {"baz", "foo"}}, + Strings{"foo"}, + }, +} + +func TestStrings_Intersect(t *testing.T) { + for _, test := range stringsIntersectTests { + t.Run("", func(t *testing.T) { + defer assertImmutableStrings(t, &test.ss)() + assert.Equal(t, test.expected, test.ss.Intersect(test.params...)) + }) + } +} diff --git a/template.go b/template.go index 469028f..34c5805 100644 --- a/template.go +++ b/template.go @@ -216,6 +216,43 @@ func (ss SliceType) FirstOr(defaultValue ElementType) ElementType { return ss[0] } +`, + "Intersect": `package functions + +// Intersect returns items that exist in all lists. +// +// It returns slice without any duplicates. +// If zero slice arguments are provided, then nil is returned. +func (ss SliceType) Intersect(slices ...SliceType) (ss2 SliceType) { + if slices == nil { + return nil + } + + var uniqs = make([]map[ElementType]struct{}, len(slices)) + for i := 0; i < len(slices); i++ { + m := make(map[ElementType]struct{}) + for _, el := range slices[i] { + m[el] = struct{}{} + } + uniqs[i] = m + } + + var containsInAll = false + for _, el := range ss.Unique() { + for _, u := range uniqs { + if _, exists := u[el]; !exists { + containsInAll = false + break + } + containsInAll = true + } + if containsInAll { + ss2 = append(ss2, el) + } + } + + return +} `, "JSONString": `package functions