git-lfs/tools/ordered_set.go
2017-06-21 16:50:19 -06:00

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
}