Skip to content

Commit 9156a7c

Browse files
committed
Align set with k/k apimachinery
This aligns the features of generic Set with the version in apimachinery. The main change is that sets can be instantiated with any comparable type, not only ordered types; as a result, SortedList() can no longer be implemented as a method, and is replaced by the List() function to match apimachinery. Signed-off-by: Stephen Kitt <skitt@redhat.com>
1 parent b8788ab commit 9156a7c

File tree

2 files changed

+45
-38
lines changed

2 files changed

+45
-38
lines changed

set/set.go

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ import (
2424
// string arrays and internal sets, and conversion logic requires public types today.
2525
type Empty struct{}
2626

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

3030
// New creates a new set.
31-
func New[E ordered](items ...E) Set[E] {
32-
ss := Set[E]{}
31+
// NOTE: type param must be explicitly instantiated if given items are empty.
32+
func New[E comparable](items ...E) Set[E] {
33+
ss := make(Set[E], len(items))
3334
ss.Insert(items...)
3435
return ss
3536
}
@@ -43,23 +44,24 @@ func (s Set[T]) Clear() Set[T] {
4344
}
4445

4546
// KeySet creates a Set[E] from a keys of a map[E](? extends interface{}).
46-
func KeySet[E ordered, A any](theMap map[E]A) Set[E] {
47+
// If the value passed in is not actually a map, this will panic.
48+
func KeySet[E comparable, A any](theMap map[E]A) Set[E] {
4749
ret := Set[E]{}
4850
for key := range theMap {
4951
ret.Insert(key)
5052
}
5153
return ret
5254
}
5355

54-
// Insert adds items to the set.
56+
// Insert adds the given items to the set.
5557
func (s Set[E]) Insert(items ...E) Set[E] {
5658
for _, item := range items {
5759
s[item] = Empty{}
5860
}
5961
return s
6062
}
6163

62-
// Delete removes all items from the set.
64+
// Delete removes the given items from the set.
6365
func (s Set[E]) Delete(items ...E) Set[E] {
6466
for _, item := range items {
6567
delete(s, item)
@@ -93,16 +95,17 @@ func (s Set[E]) HasAny(items ...E) bool {
9395
return false
9496
}
9597

96-
// Union returns a new set which includes items in either s1 or s2.
98+
// Union returns a new set which includes items in either s or s2.
9799
// For example:
98-
// s1 = {a1, a2}
100+
// s = {a1, a2}
99101
// s2 = {a3, a4}
100-
// s1.Union(s2) = {a1, a2, a3, a4}
101-
// s2.Union(s1) = {a1, a2, a3, a4}
102+
// s.Union(s2) = {a1, a2, a3, a4}
103+
// s2.Union(s) = {a1, a2, a3, a4}
102104
func (s Set[E]) Union(s2 Set[E]) Set[E] {
103-
result := Set[E]{}
104-
result.Insert(s.UnsortedList()...)
105-
result.Insert(s2.UnsortedList()...)
105+
result := s.Clone()
106+
for k2 := range s2 {
107+
result.Insert(k2)
108+
}
106109
return result
107110
}
108111

@@ -111,11 +114,11 @@ func (s Set[E]) Len() int {
111114
return len(s)
112115
}
113116

114-
// Intersection returns a new set which includes the item in BOTH s1 and s2
117+
// Intersection returns a new set which includes the item in BOTH s and s2
115118
// For example:
116-
// s1 = {a1, a2}
119+
// s = {a1, a2}
117120
// s2 = {a2, a3}
118-
// s1.Intersection(s2) = {a2}
121+
// s.Intersection(s2) = {a2}
119122
func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
120123
var walk, other Set[E]
121124
result := Set[E]{}
@@ -134,7 +137,7 @@ func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
134137
return result
135138
}
136139

137-
// IsSuperset returns true if and only if s1 is a superset of s2.
140+
// IsSuperset returns true if and only if s is a superset of s2.
138141
func (s Set[E]) IsSuperset(s2 Set[E]) bool {
139142
for item := range s2 {
140143
if !s.Has(item) {
@@ -146,10 +149,10 @@ func (s Set[E]) IsSuperset(s2 Set[E]) bool {
146149

147150
// Difference returns a set of objects that are not in s2
148151
// For example:
149-
// s1 = {a1, a2, a3}
152+
// s = {a1, a2, a3}
150153
// s2 = {a1, a2, a4, a5}
151-
// s1.Difference(s2) = {a3}
152-
// s2.Difference(s1) = {a4, a5}
154+
// s.Difference(s2) = {a3}
155+
// s2.Difference(s) = {a4, a5}
153156
func (s Set[E]) Difference(s2 Set[E]) Set[E] {
154157
result := Set[E]{}
155158
for key := range s {
@@ -160,7 +163,7 @@ func (s Set[E]) Difference(s2 Set[E]) Set[E] {
160163
return result
161164
}
162165

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

177-
// SortedList returns the contents as a sorted slice.
178-
func (s Set[E]) SortedList() []E {
179-
res := make(sortableSlice[E], 0, s.Len())
180+
// List returns the contents as a sorted T slice.
181+
//
182+
// This is a separate function and not a method because not all types supported
183+
// by Generic are ordered and only those can be sorted.
184+
func List[E ordered](s Set[E]) []E {
185+
res := make(sortableSlice[E], 0, len(s))
180186
for key := range s {
181187
res = append(res, key)
182188
}
@@ -214,10 +220,10 @@ func (s Set[T]) Clone() Set[T] {
214220

215221
// SymmetricDifference returns a set of elements which are in either of the sets, but not in their intersection.
216222
// For example:
217-
// s1 = {a1, a2, a3}
223+
// s = {a1, a2, a3}
218224
// s2 = {a1, a2, a4, a5}
219-
// s1.SymmetricDifference(s2) = {a3, a4, a5}
220-
// s2.SymmetricDifference(s1) = {a3, a4, a5}
225+
// s.SymmetricDifference(s2) = {a3, a4, a5}
226+
// s2.SymmetricDifference(s) = {a3, a4, a5}
221227
func (s Set[T]) SymmetricDifference(s2 Set[T]) Set[T] {
222228
return s.Difference(s2).Union(s2.Difference(s))
223229
}

set/set_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ func TestNewStringSetWithMultipleStrings(t *testing.T) {
100100

101101
func TestStringSetSortedList(t *testing.T) {
102102
s := New[string]("z", "y", "x", "a")
103-
if !reflect.DeepEqual(s.SortedList(), []string{"a", "x", "y", "z"}) {
104-
t.Errorf("SortedList gave unexpected result: %#v", s.SortedList())
103+
l := List(s)
104+
if !reflect.DeepEqual(l, []string{"a", "x", "y", "z"}) {
105+
t.Errorf("List gave unexpected result: %#v", l)
105106
}
106107
}
107108

@@ -122,13 +123,13 @@ func TestStringSetDifference(t *testing.T) {
122123
t.Errorf("Expected len=1: %d", len(c))
123124
}
124125
if !c.Has("3") {
125-
t.Errorf("Unexpected contents: %#v", c.SortedList())
126+
t.Errorf("Unexpected contents: %#v", List(c))
126127
}
127128
if len(d) != 2 {
128129
t.Errorf("Expected len=2: %d", len(d))
129130
}
130131
if !d.Has("4") || !d.Has("5") {
131-
t.Errorf("Unexpected contents: %#v", d.SortedList())
132+
t.Errorf("Unexpected contents: %#v", List(d))
132133
}
133134
}
134135

@@ -243,7 +244,7 @@ func TestStringUnion(t *testing.T) {
243244
}
244245

245246
if !union.Equal(test.expected) {
246-
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", union.SortedList(), test.expected.SortedList())
247+
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", List(union), List(test.expected))
247248
}
248249
}
249250
}
@@ -288,7 +289,7 @@ func TestStringIntersection(t *testing.T) {
288289
}
289290

290291
if !intersection.Equal(test.expected) {
291-
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", intersection.SortedList(), test.expected.SortedList())
292+
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", List(intersection), List(test.expected))
292293
}
293294
}
294295
}
@@ -299,11 +300,11 @@ func TestKeySet(t *testing.T) {
299300
"goodbye": "and goodnight",
300301
}
301302
expected := []string{"goodbye", "hallo"}
302-
gotList := KeySet(m).SortedList() // List() returns a sorted list
303+
gotList := List(KeySet(m)) // List() returns a sorted list
303304
if len(gotList) != len(m) {
304305
t.Fatalf("got %v elements, wanted %v", len(gotList), len(m))
305306
}
306-
for i, entry := range KeySet(m).SortedList() {
307+
for i, entry := range gotList {
307308
if entry != expected[i] {
308309
t.Errorf("got %v, expected %v", entry, expected[i])
309310
}
@@ -316,10 +317,10 @@ func TestSetSymmetricDifference(t *testing.T) {
316317
c := a.SymmetricDifference(b)
317318
d := b.SymmetricDifference(a)
318319
if !c.Equal(New("3", "4", "5")) {
319-
t.Errorf("Unexpected contents: %#v", c.SortedList())
320+
t.Errorf("Unexpected contents: %#v", List(c))
320321
}
321322
if !d.Equal(New("3", "4", "5")) {
322-
t.Errorf("Unexpected contents: %#v", d.SortedList())
323+
t.Errorf("Unexpected contents: %#v", List(d))
323324
}
324325
}
325326

0 commit comments

Comments
 (0)