218 lines
5.2 KiB
Go
218 lines
5.2 KiB
Go
|
package tools
|
||
|
|
||
|
// OrderedSet is a unique set of strings that maintains insertion order.
|
||
|
type OrderedSet struct {
|
||
|
// s is the set of strings that we're keeping track of.
|
||
|
s []string
|
||
|
// m is a mapping of string value "s" into the index "i" that that
|
||
|
// string is present in in the given "s".
|
||
|
m map[string]int
|
||
|
}
|
||
|
|
||
|
// NewOrderedSet creates an ordered set with no values.
|
||
|
func NewOrderedSet() *OrderedSet {
|
||
|
return NewOrderedSetWithCapacity(0)
|
||
|
}
|
||
|
|
||
|
// NewOrderedSetWithCapacity creates a new ordered set with no values. The
|
||
|
// returned ordered set can be appended to "capacity" number of times before it
|
||
|
// grows internally.
|
||
|
func NewOrderedSetWithCapacity(capacity int) *OrderedSet {
|
||
|
return &OrderedSet{
|
||
|
s: make([]string, 0, capacity),
|
||
|
m: make(map[string]int, capacity),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewOrderedSetFromSlice returns a new ordered set with the elements given in
|
||
|
// the slice "s".
|
||
|
func NewOrderedSetFromSlice(s []string) *OrderedSet {
|
||
|
set := NewOrderedSetWithCapacity(len(s))
|
||
|
for _, e := range s {
|
||
|
set.Add(e)
|
||
|
}
|
||
|
|
||
|
return set
|
||
|
}
|
||
|
|
||
|
// Add adds the given element "i" to the ordered set, unless the element is
|
||
|
// already present. It returns whether or not the element was added.
|
||
|
func (s *OrderedSet) Add(i string) bool {
|
||
|
if _, ok := s.m[i]; ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
s.s = append(s.s, i)
|
||
|
s.m[i] = len(s.s) - 1
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Contains returns whether or not the given "i" is contained in this ordered
|
||
|
// set. It is a constant-time operation.
|
||
|
func (s *OrderedSet) Contains(i string) bool {
|
||
|
if _, ok := s.m[i]; ok {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// ContainsAll returns whether or not all of the given items in "i" are present
|
||
|
// in the ordered set.
|
||
|
func (s *OrderedSet) ContainsAll(i ...string) bool {
|
||
|
for _, item := range i {
|
||
|
if !s.Contains(item) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// IsSubset returns whether other is a subset of this ordered set. In other
|
||
|
// words, it returns whether or not all of the elements in "other" are also
|
||
|
// present in this set.
|
||
|
func (s *OrderedSet) IsSubset(other *OrderedSet) bool {
|
||
|
for _, i := range other.s {
|
||
|
if !s.Contains(i) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// IsSuperset returns whether or not this set is a superset of "other". In other
|
||
|
// words, it returns whether or not all of the elements in this set are also in
|
||
|
// the set "other".
|
||
|
func (s *OrderedSet) IsSuperset(other *OrderedSet) bool {
|
||
|
return other.IsSubset(s)
|
||
|
}
|
||
|
|
||
|
// Union returns a union of this set with the given set "other". It returns the
|
||
|
// items that are in either set while maintaining uniqueness constraints. It
|
||
|
// preserves ordered within each set, and orders the elements in this set before
|
||
|
// the elements in "other".
|
||
|
//
|
||
|
// It is an O(n+m) operation.
|
||
|
func (s *OrderedSet) Union(other *OrderedSet) *OrderedSet {
|
||
|
union := NewOrderedSetWithCapacity(other.Cardinality() + s.Cardinality())
|
||
|
|
||
|
for _, e := range s.s {
|
||
|
union.Add(e)
|
||
|
}
|
||
|
for _, e := range other.s {
|
||
|
union.Add(e)
|
||
|
}
|
||
|
|
||
|
return union
|
||
|
}
|
||
|
|
||
|
// Intersect returns the elements that are in both this set and then given
|
||
|
// "ordered" set. It is an O(min(n, m)) (in other words, O(n)) operation.
|
||
|
func (s *OrderedSet) Intersect(other *OrderedSet) *OrderedSet {
|
||
|
intersection := NewOrderedSetWithCapacity(MinInt(
|
||
|
s.Cardinality(), other.Cardinality()))
|
||
|
|
||
|
if s.Cardinality() < other.Cardinality() {
|
||
|
for _, elem := range s.s {
|
||
|
if other.Contains(elem) {
|
||
|
intersection.Add(elem)
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for _, elem := range other.s {
|
||
|
if s.Contains(elem) {
|
||
|
intersection.Add(elem)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return intersection
|
||
|
}
|
||
|
|
||
|
// Difference returns the elements that are in this set, but not included in
|
||
|
// other.
|
||
|
func (s *OrderedSet) Difference(other *OrderedSet) *OrderedSet {
|
||
|
diff := NewOrderedSetWithCapacity(s.Cardinality())
|
||
|
for _, e := range s.s {
|
||
|
if !other.Contains(e) {
|
||
|
diff.Add(e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return diff
|
||
|
}
|
||
|
|
||
|
// SymmetricDifference returns the elements that are not present in both sets.
|
||
|
func (s *OrderedSet) SymmetricDifference(other *OrderedSet) *OrderedSet {
|
||
|
left := s.Difference(other)
|
||
|
right := other.Difference(s)
|
||
|
|
||
|
return left.Union(right)
|
||
|
}
|
||
|
|
||
|
// Clear removes all elements from this set.
|
||
|
func (s *OrderedSet) Clear() {
|
||
|
s.s = make([]string, 0)
|
||
|
s.m = make(map[string]int, 0)
|
||
|
}
|
||
|
|
||
|
// Remove removes the given element "i" from this set.
|
||
|
func (s *OrderedSet) Remove(i string) {
|
||
|
idx, ok := s.m[i]
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rest := MinInt(idx+1, len(s.s)-1)
|
||
|
|
||
|
s.s = append(s.s[:idx], s.s[rest:]...)
|
||
|
for _, e := range s.s[rest:] {
|
||
|
s.m[e] = s.m[e] - 1
|
||
|
}
|
||
|
delete(s.m, i)
|
||
|
}
|
||
|
|
||
|
// Cardinality returns the cardinality of this set.
|
||
|
func (s *OrderedSet) Cardinality() int {
|
||
|
return len(s.s)
|
||
|
}
|
||
|
|
||
|
// Iter returns a channel which yields the elements in this set in insertion
|
||
|
// order.
|
||
|
func (s *OrderedSet) Iter() <-chan string {
|
||
|
c := make(chan string)
|
||
|
go func() {
|
||
|
for _, i := range s.s {
|
||
|
c <- i
|
||
|
}
|
||
|
close(c)
|
||
|
}()
|
||
|
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// Equal returns whether this element has the same number, identity and ordering
|
||
|
// elements as given in "other".
|
||
|
func (s *OrderedSet) Equal(other *OrderedSet) bool {
|
||
|
if s.Cardinality() != other.Cardinality() {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
for e, i := range s.m {
|
||
|
if ci, ok := other.m[e]; !ok || ci != i {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Clone returns a deep copy of this set.
|
||
|
func (s *OrderedSet) Clone() *OrderedSet {
|
||
|
clone := NewOrderedSetWithCapacity(s.Cardinality())
|
||
|
for _, i := range s.s {
|
||
|
clone.Add(i)
|
||
|
}
|
||
|
return clone
|
||
|
}
|