Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align set with k/k apimachinery #294

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 34 additions & 28 deletions set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,36 @@ import (
// string arrays and internal sets, and conversion logic requires public types today.
type Empty struct{}

// Set is a set of the same type elements, implemented via map[ordered]struct{} for minimal memory consumption.
type Set[E ordered] map[E]Empty
// Set is a set of the same type elements, implemented via map[comparable]struct{} for minimal memory consumption.
type Set[E comparable] map[E]Empty

// New creates a new set.
func New[E ordered](items ...E) Set[E] {
ss := Set[E]{}
// NOTE: type param must be explicitly instantiated if given items are empty.
func New[E comparable](items ...E) Set[E] {
ss := make(Set[E], len(items))
ss.Insert(items...)
return ss
}

// KeySet creates a Set[E] from a keys of a map[E](? extends interface{}).
func KeySet[E ordered, A any](theMap map[E]A) Set[E] {
// If the value passed in is not actually a map, this will panic.
func KeySet[E comparable, A any](theMap map[E]A) Set[E] {
ret := Set[E]{}
for key := range theMap {
ret.Insert(key)
}
return ret
}

// Insert adds items to the set.
// Insert adds the given items to the set.
func (s Set[E]) Insert(items ...E) Set[E] {
for _, item := range items {
s[item] = Empty{}
}
return s
}

// Delete removes all items from the set.
// Delete removes the given items from the set.
func (s Set[E]) Delete(items ...E) Set[E] {
for _, item := range items {
delete(s, item)
Expand Down Expand Up @@ -85,16 +87,17 @@ func (s Set[E]) HasAny(items ...E) bool {
return false
}

// Union returns a new set which includes items in either s1 or s2.
// Union returns a new set which includes items in either s or s2.
// For example:
// s1 = {a1, a2}
// s = {a1, a2}
// s2 = {a3, a4}
// s1.Union(s2) = {a1, a2, a3, a4}
// s2.Union(s1) = {a1, a2, a3, a4}
// s.Union(s2) = {a1, a2, a3, a4}
// s2.Union(s) = {a1, a2, a3, a4}
func (s Set[E]) Union(s2 Set[E]) Set[E] {
result := Set[E]{}
result.Insert(s.UnsortedList()...)
result.Insert(s2.UnsortedList()...)
result := s.Clone()
for k2 := range s2 {
result.Insert(k2)
}
return result
}

Expand All @@ -103,11 +106,11 @@ func (s Set[E]) Len() int {
return len(s)
}

// Intersection returns a new set which includes the item in BOTH s1 and s2
// Intersection returns a new set which includes the item in BOTH s and s2
// For example:
// s1 = {a1, a2}
// s = {a1, a2}
// s2 = {a2, a3}
// s1.Intersection(s2) = {a2}
// s.Intersection(s2) = {a2}
func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
var walk, other Set[E]
result := Set[E]{}
Expand All @@ -126,7 +129,7 @@ func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
return result
}

// IsSuperset returns true if and only if s1 is a superset of s2.
// IsSuperset returns true if and only if s is a superset of s2.
func (s Set[E]) IsSuperset(s2 Set[E]) bool {
for item := range s2 {
if !s.Has(item) {
Expand All @@ -138,10 +141,10 @@ func (s Set[E]) IsSuperset(s2 Set[E]) bool {

// Difference returns a set of objects that are not in s2
// For example:
// s1 = {a1, a2, a3}
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s1.Difference(s2) = {a3}
// s2.Difference(s1) = {a4, a5}
// s.Difference(s2) = {a3}
// s2.Difference(s) = {a4, a5}
func (s Set[E]) Difference(s2 Set[E]) Set[E] {
result := Set[E]{}
for key := range s {
Expand All @@ -152,7 +155,7 @@ func (s Set[E]) Difference(s2 Set[E]) Set[E] {
return result
}

// Equal returns true if and only if s1 is equal (as a set) to s2.
// Equal returns true if and only if s is equal (as a set) to s2.
// Two sets are equal if their membership is identical.
func (s Set[E]) Equal(s2 Set[E]) bool {
return s.Len() == s2.Len() && s.IsSuperset(s2)
Expand All @@ -166,9 +169,12 @@ func (s sortableSlice[E]) Len() int {
func (s sortableSlice[E]) Less(i, j int) bool { return s[i] < s[j] }
func (s sortableSlice[E]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// SortedList returns the contents as a sorted slice.
func (s Set[E]) SortedList() []E {
res := make(sortableSlice[E], 0, s.Len())
// List returns the contents as a sorted T slice.
//
// This is a separate function and not a method because not all types supported
// by Generic are ordered and only those can be sorted.
func List[E ordered](s Set[E]) []E {
res := make(sortableSlice[E], 0, len(s))
for key := range s {
res = append(res, key)
}
Expand Down Expand Up @@ -206,10 +212,10 @@ func (s Set[T]) Clone() Set[T] {

// SymmetricDifference returns a set of elements which are in either of the sets, but not in their intersection.
// For example:
// s1 = {a1, a2, a3}
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s1.SymmetricDifference(s2) = {a3, a4, a5}
// s2.SymmetricDifference(s1) = {a3, a4, a5}
// s.SymmetricDifference(s2) = {a3, a4, a5}
// s2.SymmetricDifference(s) = {a3, a4, a5}
func (s Set[T]) SymmetricDifference(s2 Set[T]) Set[T] {
return s.Difference(s2).Union(s2.Difference(s))
}
21 changes: 11 additions & 10 deletions set/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ func TestNewStringSetWithMultipleStrings(t *testing.T) {

func TestStringSetSortedList(t *testing.T) {
s := New[string]("z", "y", "x", "a")
if !reflect.DeepEqual(s.SortedList(), []string{"a", "x", "y", "z"}) {
t.Errorf("SortedList gave unexpected result: %#v", s.SortedList())
l := List(s)
if !reflect.DeepEqual(l, []string{"a", "x", "y", "z"}) {
t.Errorf("List gave unexpected result: %#v", l)
}
}

Expand All @@ -122,13 +123,13 @@ func TestStringSetDifference(t *testing.T) {
t.Errorf("Expected len=1: %d", len(c))
}
if !c.Has("3") {
t.Errorf("Unexpected contents: %#v", c.SortedList())
t.Errorf("Unexpected contents: %#v", List(c))
}
if len(d) != 2 {
t.Errorf("Expected len=2: %d", len(d))
}
if !d.Has("4") || !d.Has("5") {
t.Errorf("Unexpected contents: %#v", d.SortedList())
t.Errorf("Unexpected contents: %#v", List(d))
}
}

Expand Down Expand Up @@ -239,7 +240,7 @@ func TestStringUnion(t *testing.T) {
}

if !union.Equal(test.expected) {
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", union.SortedList(), test.expected.SortedList())
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", List(union), List(test.expected))
}
}
}
Expand Down Expand Up @@ -284,7 +285,7 @@ func TestStringIntersection(t *testing.T) {
}

if !intersection.Equal(test.expected) {
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", intersection.SortedList(), test.expected.SortedList())
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", List(intersection), List(test.expected))
}
}
}
Expand All @@ -295,11 +296,11 @@ func TestKeySet(t *testing.T) {
"goodbye": "and goodnight",
}
expected := []string{"goodbye", "hallo"}
gotList := KeySet(m).SortedList() // List() returns a sorted list
gotList := List(KeySet(m)) // List() returns a sorted list
if len(gotList) != len(m) {
t.Fatalf("got %v elements, wanted %v", len(gotList), len(m))
}
for i, entry := range KeySet(m).SortedList() {
for i, entry := range gotList {
if entry != expected[i] {
t.Errorf("got %v, expected %v", entry, expected[i])
}
Expand All @@ -312,10 +313,10 @@ func TestSetSymmetricDifference(t *testing.T) {
c := a.SymmetricDifference(b)
d := b.SymmetricDifference(a)
if !c.Equal(New("3", "4", "5")) {
t.Errorf("Unexpected contents: %#v", c.SortedList())
t.Errorf("Unexpected contents: %#v", List(c))
}
if !d.Equal(New("3", "4", "5")) {
t.Errorf("Unexpected contents: %#v", d.SortedList())
t.Errorf("Unexpected contents: %#v", List(d))
}
}

Expand Down