diff --git a/set/set.go b/set/set.go index fc25b2fe..e7131c61 100644 --- a/set/set.go +++ b/set/set.go @@ -24,18 +24,20 @@ 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) @@ -43,7 +45,7 @@ func KeySet[E ordered, A any](theMap map[E]A) Set[E] { 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{} @@ -51,7 +53,7 @@ func (s Set[E]) Insert(items ...E) Set[E] { 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) @@ -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 } @@ -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]{} @@ -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) { @@ -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 { @@ -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) @@ -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) } @@ -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)) } diff --git a/set/set_test.go b/set/set_test.go index a4bef5c0..b5a1710b 100644 --- a/set/set_test.go +++ b/set/set_test.go @@ -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) } } @@ -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)) } } @@ -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)) } } } @@ -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)) } } } @@ -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]) } @@ -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)) } }