From 1ab23d2d031c6cd354226e82c9e511bbf8b49598 Mon Sep 17 00:00:00 2001 From: michalPycia Date: Fri, 21 Jun 2024 11:14:17 +0200 Subject: [PATCH 1/2] added new functions --- sliceutil/sets.go | 18 +++++++++++++++ sliceutil/sets_test.go | 31 +++++++++++++++++++++++++ sliceutil/sliceutil.go | 24 ++++++++++++++++++++ sliceutil/sliceutil_test.go | 45 +++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/sliceutil/sets.go b/sliceutil/sets.go index b216cbe..d42b0c7 100644 --- a/sliceutil/sets.go +++ b/sliceutil/sets.go @@ -57,3 +57,21 @@ func Intersection[T comparable](a, b []T) []T { } return inter } + +// IntersectionOfMany returns the elements common to all arguments +func IntersectionOfMany[T comparable](slices ...[]T) []T { + if len(slices) == 1 { + return slices[0] + } + common := slices[0] + for i, s := range slices { + if i == 0 { + continue + } + common = Intersection(common, s) + if len(common) == 0 { + break + } + } + return common +} diff --git a/sliceutil/sets_test.go b/sliceutil/sets_test.go index f41345a..e884bc2 100644 --- a/sliceutil/sets_test.go +++ b/sliceutil/sets_test.go @@ -155,6 +155,37 @@ func TestIntersection(t *testing.T) { } } +func TestIntersectionOfMany(t *testing.T) { + type ciTest struct { + name string + a []int64 + b []int64 + c []int64 + result []int64 + } + tests := []ciTest{ + { + name: "EmptyLists", + result: []int64{}, + }, { + name: "three", + a: []int64{1, 2, 3}, + b: []int64{3}, + c: []int64{3, 4, 5}, + result: []int64{3}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := IntersectionOfMany(test.a, test.b) + + if !reflect.DeepEqual(result, test.result) { + t.Errorf("result wrong\ngot: %#v\nwant: %#v\n", result, test.result) + } + }) + } +} + func BenchmarkComplement_equal(b *testing.B) { listA := []int64{1, 2, 3} listB := []int64{1, 2, 3} diff --git a/sliceutil/sliceutil.go b/sliceutil/sliceutil.go index 0b24043..16a861f 100644 --- a/sliceutil/sliceutil.go +++ b/sliceutil/sliceutil.go @@ -93,6 +93,16 @@ func Contains[T comparable](tt []T, item T) bool { return false } +// ContainsAny checks if slice contain element +func ContainsAny[T comparable](slice []T, elements ...T) bool { + for _, element := range elements { + if Contains(slice, element) { + return true + } + } + return false +} + // InFoldedStringSlice reports whether str is within list(case-insensitive) func InFoldedStringSlice(list []string, str string) bool { for _, item := range list { @@ -201,3 +211,17 @@ func Values[T comparable, N any](tt []T, fn func(T) N) []N { return ret } + +// IsSubset returns if all elements of 'slice' are in 'set' +func IsSubset[T comparable](slice, subset []T) bool { + subsetMap := make(map[T]bool, len(subset)) + for _, v := range subset { + subsetMap[v] = true + } + for _, v := range slice { + if !subsetMap[v] { + return false + } + } + return true +} diff --git a/sliceutil/sliceutil_test.go b/sliceutil/sliceutil_test.go index 7df4418..bc30e16 100644 --- a/sliceutil/sliceutil_test.go +++ b/sliceutil/sliceutil_test.go @@ -307,6 +307,51 @@ func TestItemInSlice_String(t *testing.T) { } } +func TestContainsAny(t *testing.T) { + tests := []struct { + list []string + find string + find2 string + expected bool + }{ + {[]string{"hello"}, "hello", "world", true}, + {[]string{"hello"}, "hell", "world", false}, + {[]string{"hello", "world", "test"}, "world", "potato", true}, + {[]string{"hello", "world", "test"}, "", "potato", false}, + {[]string{}, "", "", false}, + } + + for i, tc := range tests { + t.Run(fmt.Sprintf("test-%v", i), func(t *testing.T) { + got := ContainsAny(tc.list, tc.find) + if got != tc.expected { + t.Errorf(diff.Cmp(tc.expected, got)) + } + }) + } +} + +func TestIsSubset(t *testing.T) { + tests := []struct { + set []int + subset []int + expected bool + }{ + {[]int{1, 2, 3, 4}, []int{2, 3}, true}, + {[]int{1, 2, 3, 4}, []int{2, 3, 8}, false}, + {[]int{1, 2, 3, 4}, []int{1, 2, 3, 4}, false}, + } + + for i, tc := range tests { + t.Run(fmt.Sprintf("test-%v", i), func(t *testing.T) { + got := IsSubset(tc.set, tc.subset) + if got != tc.expected { + t.Errorf(diff.Cmp(tc.expected, got)) + } + }) + } +} + func TestInFoldedStringSlice(t *testing.T) { tests := []struct { list []string From 5cd10ab64f3b17c5855066f81420972bf0966149 Mon Sep 17 00:00:00 2001 From: michalPycia Date: Fri, 21 Jun 2024 13:41:06 +0200 Subject: [PATCH 2/2] review fix --- sliceutil/sets.go | 7 ++----- sliceutil/sets_test.go | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sliceutil/sets.go b/sliceutil/sets.go index d42b0c7..438a16d 100644 --- a/sliceutil/sets.go +++ b/sliceutil/sets.go @@ -60,13 +60,10 @@ func Intersection[T comparable](a, b []T) []T { // IntersectionOfMany returns the elements common to all arguments func IntersectionOfMany[T comparable](slices ...[]T) []T { - if len(slices) == 1 { - return slices[0] - } - common := slices[0] + var common []T for i, s := range slices { if i == 0 { - continue + common = s } common = Intersection(common, s) if len(common) == 0 { diff --git a/sliceutil/sets_test.go b/sliceutil/sets_test.go index e884bc2..85efcab 100644 --- a/sliceutil/sets_test.go +++ b/sliceutil/sets_test.go @@ -167,7 +167,8 @@ func TestIntersectionOfMany(t *testing.T) { { name: "EmptyLists", result: []int64{}, - }, { + }, + { name: "three", a: []int64{1, 2, 3}, b: []int64{3},