Skip to content

Commit 93a9af8

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 <[email protected]>
1 parent cf03d44 commit 93a9af8

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,34 +24,36 @@ 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
}
3637

3738
// KeySet creates a Set[E] from a keys of a map[E](? extends interface{}).
38-
func KeySet[E ordered, A any](theMap map[E]A) Set[E] {
39+
// If the value passed in is not actually a map, this will panic.
40+
func KeySet[E comparable, A any](theMap map[E]A) Set[E] {
3941
ret := Set[E]{}
4042
for key := range theMap {
4143
ret.Insert(key)
4244
}
4345
return ret
4446
}
4547

46-
// Insert adds items to the set.
48+
// Insert adds the given items to the set.
4749
func (s Set[E]) Insert(items ...E) Set[E] {
4850
for _, item := range items {
4951
s[item] = Empty{}
5052
}
5153
return s
5254
}
5355

54-
// Delete removes all items from the set.
56+
// Delete removes the given items from the set.
5557
func (s Set[E]) Delete(items ...E) Set[E] {
5658
for _, item := range items {
5759
delete(s, item)
@@ -85,16 +87,17 @@ func (s Set[E]) HasAny(items ...E) bool {
8587
return false
8688
}
8789

88-
// Union returns a new set which includes items in either s1 or s2.
90+
// Union returns a new set which includes items in either s or s2.
8991
// For example:
90-
// s1 = {a1, a2}
92+
// s = {a1, a2}
9193
// s2 = {a3, a4}
92-
// s1.Union(s2) = {a1, a2, a3, a4}
93-
// s2.Union(s1) = {a1, a2, a3, a4}
94+
// s.Union(s2) = {a1, a2, a3, a4}
95+
// s2.Union(s) = {a1, a2, a3, a4}
9496
func (s Set[E]) Union(s2 Set[E]) Set[E] {
95-
result := Set[E]{}
96-
result.Insert(s.UnsortedList()...)
97-
result.Insert(s2.UnsortedList()...)
97+
result := s.Clone()
98+
for k2 := range s2 {
99+
result.Insert(k2)
100+
}
98101
return result
99102
}
100103

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

106-
// Intersection returns a new set which includes the item in BOTH s1 and s2
109+
// Intersection returns a new set which includes the item in BOTH s and s2
107110
// For example:
108-
// s1 = {a1, a2}
111+
// s = {a1, a2}
109112
// s2 = {a2, a3}
110-
// s1.Intersection(s2) = {a2}
113+
// s.Intersection(s2) = {a2}
111114
func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
112115
var walk, other Set[E]
113116
result := Set[E]{}
@@ -126,7 +129,7 @@ func (s Set[E]) Intersection(s2 Set[E]) Set[E] {
126129
return result
127130
}
128131

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

139142
// Difference returns a set of objects that are not in s2
140143
// For example:
141-
// s1 = {a1, a2, a3}
144+
// s = {a1, a2, a3}
142145
// s2 = {a1, a2, a4, a5}
143-
// s1.Difference(s2) = {a3}
144-
// s2.Difference(s1) = {a4, a5}
146+
// s.Difference(s2) = {a3}
147+
// s2.Difference(s) = {a4, a5}
145148
func (s Set[E]) Difference(s2 Set[E]) Set[E] {
146149
result := Set[E]{}
147150
for key := range s {
@@ -152,7 +155,7 @@ func (s Set[E]) Difference(s2 Set[E]) Set[E] {
152155
return result
153156
}
154157

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

169-
// SortedList returns the contents as a sorted slice.
170-
func (s Set[E]) SortedList() []E {
171-
res := make(sortableSlice[E], 0, s.Len())
172+
// List returns the contents as a sorted T slice.
173+
//
174+
// This is a separate function and not a method because not all types supported
175+
// by Generic are ordered and only those can be sorted.
176+
func List[E ordered](s Set[E]) []E {
177+
res := make(sortableSlice[E], 0, len(s))
172178
for key := range s {
173179
res = append(res, key)
174180
}
@@ -206,10 +212,10 @@ func (s Set[T]) Clone() Set[T] {
206212

207213
// SymmetricDifference returns a set of elements which are in either of the sets, but not in their intersection.
208214
// For example:
209-
// s1 = {a1, a2, a3}
215+
// s = {a1, a2, a3}
210216
// s2 = {a1, a2, a4, a5}
211-
// s1.SymmetricDifference(s2) = {a3, a4, a5}
212-
// s2.SymmetricDifference(s1) = {a3, a4, a5}
217+
// s.SymmetricDifference(s2) = {a3, a4, a5}
218+
// s2.SymmetricDifference(s) = {a3, a4, a5}
213219
func (s Set[T]) SymmetricDifference(s2 Set[T]) Set[T] {
214220
return s.Difference(s2).Union(s2.Difference(s))
215221
}

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

@@ -239,7 +240,7 @@ func TestStringUnion(t *testing.T) {
239240
}
240241

241242
if !union.Equal(test.expected) {
242-
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", union.SortedList(), test.expected.SortedList())
243+
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", List(union), List(test.expected))
243244
}
244245
}
245246
}
@@ -284,7 +285,7 @@ func TestStringIntersection(t *testing.T) {
284285
}
285286

286287
if !intersection.Equal(test.expected) {
287-
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", intersection.SortedList(), test.expected.SortedList())
288+
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", List(intersection), List(test.expected))
288289
}
289290
}
290291
}
@@ -295,11 +296,11 @@ func TestKeySet(t *testing.T) {
295296
"goodbye": "and goodnight",
296297
}
297298
expected := []string{"goodbye", "hallo"}
298-
gotList := KeySet(m).SortedList() // List() returns a sorted list
299+
gotList := List(KeySet(m)) // List() returns a sorted list
299300
if len(gotList) != len(m) {
300301
t.Fatalf("got %v elements, wanted %v", len(gotList), len(m))
301302
}
302-
for i, entry := range KeySet(m).SortedList() {
303+
for i, entry := range gotList {
303304
if entry != expected[i] {
304305
t.Errorf("got %v, expected %v", entry, expected[i])
305306
}
@@ -312,10 +313,10 @@ func TestSetSymmetricDifference(t *testing.T) {
312313
c := a.SymmetricDifference(b)
313314
d := b.SymmetricDifference(a)
314315
if !c.Equal(New("3", "4", "5")) {
315-
t.Errorf("Unexpected contents: %#v", c.SortedList())
316+
t.Errorf("Unexpected contents: %#v", List(c))
316317
}
317318
if !d.Equal(New("3", "4", "5")) {
318-
t.Errorf("Unexpected contents: %#v", d.SortedList())
319+
t.Errorf("Unexpected contents: %#v", List(d))
319320
}
320321
}
321322

0 commit comments

Comments
 (0)