Skip to content

Commit 761a483

Browse files
committed
Implement a ranked set based on key and rank
This change adds a ranked 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 ranked based on the time and deduped based on the name. Signed-off-by: Monis Khan <[email protected]>
1 parent f9f2130 commit 761a483

File tree

2 files changed

+435
-0
lines changed

2 files changed

+435
-0
lines changed

pkg/util/rankedset/rankedset.go

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

0 commit comments

Comments
 (0)