Skip to content

Commit 804c8d6

Browse files
committed
Implement a sorted set based on key and rank
This change adds a sorted set that uses a key (string) to determine uniqueness and a rank (int64) to determine ordering. For example, a structure with a name and time could be sorted based on the time and deduped based on the name. Signed-off-by: Monis Khan <mkhan@redhat.com>
1 parent 3c9b776 commit 804c8d6

File tree

2 files changed

+503
-0
lines changed

2 files changed

+503
-0
lines changed

pkg/util/sortedset/sortedset.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package sortedset
2+
3+
import "github.com/google/btree"
4+
5+
// SetItem represents a single object in a SortedSet.
6+
type SetItem interface {
7+
// Key returns the unique identifier for this item.
8+
Key() string
9+
// Rank is used to sort items.
10+
// Items with the same rank are sorted lexicographically based on Key.
11+
// If sorting only by Key is desired, implementors should embed NoRank and override Key.
12+
Rank() int64
13+
}
14+
15+
// SortedSet stores SetItems based on Key (uniqueness) and Rank (sorting).
16+
type SortedSet struct {
17+
sorted *btree.BTree
18+
set map[string]*treeItem
19+
}
20+
21+
// SetString implements SetItem using a string.
22+
// It has two main uses:
23+
// 1. If all items in a SortedSet are SetStrings, the set becomes a store of unique strings sorted lexicographically.
24+
// 2. It serves as a Key item that can be passed into methods that ignore Rank such as SortedSet.Remove.
25+
type SetString string
26+
27+
func (s SetString) Key() string {
28+
return string(s)
29+
}
30+
31+
func (s SetString) Rank() int64 {
32+
return 0
33+
}
34+
35+
// NoRank is embedded in custom structs so that SortedSet will only sort by Key.
36+
type NoRank struct{}
37+
38+
func (*NoRank) Key() string {
39+
panic("implementors should embed NoRank and implement Key to sort items lexicographically")
40+
}
41+
42+
func (*NoRank) Rank() int64 {
43+
return 0
44+
}
45+
46+
func New() *SortedSet {
47+
return &SortedSet{
48+
sorted: btree.New(32),
49+
set: make(map[string]*treeItem),
50+
}
51+
}
52+
53+
// Add inserts the item into the set.
54+
// If an item with the same Key existed in the set, it is removed and returned.
55+
func (s *SortedSet) Add(item SetItem) SetItem {
56+
old := s.Remove(item)
57+
58+
key := item.Key()
59+
value := &treeItem{item: item}
60+
61+
s.sorted.ReplaceOrInsert(value) // should always return nil because we call remove first
62+
s.set[key] = value
63+
64+
return old
65+
}
66+
67+
// Remove deletes the item from the set based on Key (Rank is ignored).
68+
// The removed item is returned if it existed in the set.
69+
func (s *SortedSet) Remove(item SetItem) SetItem {
70+
key := item.Key()
71+
value, ok := s.set[key]
72+
if !ok {
73+
return nil
74+
}
75+
76+
s.sorted.Delete(value) // should always return the same data as value (non-nil)
77+
delete(s.set, key)
78+
79+
return value.item
80+
}
81+
82+
func (s *SortedSet) Min() SetItem {
83+
if min := s.sorted.Min(); min != nil {
84+
return min.(*treeItem).item
85+
}
86+
return nil
87+
}
88+
89+
func (s *SortedSet) Max() SetItem {
90+
if max := s.sorted.Max(); max != nil {
91+
return max.(*treeItem).item
92+
}
93+
return nil
94+
}
95+
96+
func (s *SortedSet) Len() int {
97+
return len(s.set)
98+
}
99+
100+
func (s *SortedSet) Get(item SetItem) SetItem {
101+
if value, ok := s.set[item.Key()]; ok {
102+
return value.item
103+
}
104+
return nil
105+
}
106+
107+
func (s *SortedSet) Has(item SetItem) bool {
108+
_, ok := s.set[item.Key()]
109+
return ok
110+
}
111+
112+
// List returns all items in the set in sorted order.
113+
// If remove is set to true, the returned items are removed from the set.
114+
func (s *SortedSet) List(remove bool) []SetItem {
115+
return s.ascend(
116+
func(item SetItem) bool {
117+
return true
118+
},
119+
remove,
120+
)
121+
}
122+
123+
// LessThan returns all items less than the given rank.
124+
// If remove is set to true, the returned items are removed from the set.
125+
func (s *SortedSet) LessThan(rank int64, remove bool) []SetItem {
126+
return s.ascend(
127+
func(item SetItem) bool {
128+
return item.Rank() < rank
129+
},
130+
remove,
131+
)
132+
}
133+
134+
// setItemIterator allows callers of ascend to iterate in-order over the set.
135+
// When this function returns false, iteration will stop.
136+
type setItemIterator func(item SetItem) bool
137+
138+
func (s *SortedSet) ascend(iterator setItemIterator, remove bool) []SetItem {
139+
var items []SetItem
140+
s.sorted.Ascend(func(i btree.Item) bool {
141+
item := i.(*treeItem).item
142+
if !iterator(item) {
143+
return false
144+
}
145+
items = append(items, item)
146+
return true
147+
})
148+
// remove after Ascend since it is probably not safe to delete while iterating
149+
if remove {
150+
for _, item := range items {
151+
s.Remove(item)
152+
}
153+
}
154+
return items
155+
}
156+
157+
var _ btree.Item = &treeItem{}
158+
159+
type treeItem struct {
160+
item SetItem
161+
}
162+
163+
func (i *treeItem) Less(than btree.Item) bool {
164+
other := than.(*treeItem).item
165+
166+
selfKey := i.item.Key()
167+
otherKey := other.Key()
168+
169+
// !a.Less(b) && !b.Less(a) means a == b
170+
if selfKey == otherKey {
171+
return false
172+
}
173+
174+
selfRank := i.item.Rank()
175+
otherRank := other.Rank()
176+
177+
if selfRank == otherRank {
178+
return selfKey < otherKey
179+
}
180+
181+
return selfRank < otherRank
182+
}

0 commit comments

Comments
 (0)