1// Copyright 2017 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14package sets 15 16import ( 17 "fmt" 18 "sort" 19 "strings" 20) 21 22// StringSet stores a set of unique string elements. 23type StringSet struct { 24 set map[string]present 25} 26 27// NewStringSet creates a StringSet containing the supplied initial string elements. 28func NewStringSet(elements ...string) *StringSet { 29 s := &StringSet{} 30 s.set = make(map[string]present) 31 s.Insert(elements...) 32 return s 33} 34 35// Copy returns a newly allocated copy of the supplied StringSet. 36func (s *StringSet) Copy() *StringSet { 37 c := NewStringSet() 38 if s != nil { 39 for e := range s.set { 40 c.set[e] = present{} 41 } 42 } 43 return c 44} 45 46// Insert zero or more string elements into the StringSet. 47// As expected for a Set, elements already present in the StringSet are 48// simply ignored. 49func (s *StringSet) Insert(elements ...string) { 50 for _, e := range elements { 51 s.set[e] = present{} 52 } 53} 54 55// Delete zero or more string elements from the StringSet. 56// Any elements not present in the StringSet are simply ignored. 57func (s *StringSet) Delete(elements ...string) { 58 for _, e := range elements { 59 delete(s.set, e) 60 } 61} 62 63// Intersect returns a new StringSet containing the intersection of the 64// receiver and argument StringSets. Returns an empty set if the argument is nil. 65func (s *StringSet) Intersect(other *StringSet) *StringSet { 66 if other == nil { 67 return NewStringSet() 68 } 69 70 // Point a and b to the maps, setting a to the smaller of the two. 71 a, b := s.set, other.set 72 if len(b) < len(a) { 73 a, b = b, a 74 } 75 76 // Perform the intersection. 77 intersect := NewStringSet() 78 for e := range a { 79 if _, ok := b[e]; ok { 80 intersect.set[e] = present{} 81 } 82 } 83 return intersect 84} 85 86// Disjoint returns true if the intersection of the receiver and the argument 87// StringSets is the empty set. Returns true if the argument is nil or either 88// StringSet is the empty set. 89func (s *StringSet) Disjoint(other *StringSet) bool { 90 if other == nil || len(other.set) == 0 || len(s.set) == 0 { 91 return true 92 } 93 94 // Point a and b to the maps, setting a to the smaller of the two. 95 a, b := s.set, other.set 96 if len(b) < len(a) { 97 a, b = b, a 98 } 99 100 // Check for non-empty intersection. 101 for e := range a { 102 if _, ok := b[e]; ok { 103 return false // Early-exit because intersecting. 104 } 105 } 106 return true 107} 108 109// Difference returns a new StringSet containing the elements in the receiver 110// that are not present in the argument StringSet. Returns a copy of the 111// receiver if the argument is nil. 112func (s *StringSet) Difference(other *StringSet) *StringSet { 113 if other == nil { 114 return s.Copy() 115 } 116 117 // Insert only the elements in the receiver that are not present in the 118 // argument StringSet. 119 diff := NewStringSet() 120 for e := range s.set { 121 if _, ok := other.set[e]; !ok { 122 diff.set[e] = present{} 123 } 124 } 125 return diff 126} 127 128// Unique returns a new StringSet containing the elements in the receiver 129// that are not present in the argument StringSet *and* the elements in the 130// argument StringSet that are not in the receiver (which is the union of two 131// disjoint sets). Returns a copy of the 132// receiver if the argument is nil. 133func (s *StringSet) Unique(other *StringSet) *StringSet { 134 if other == nil { 135 return s.Copy() 136 } 137 138 sNotInOther := s.Difference(other) 139 otherNotInS := other.Difference(s) 140 141 // Duplicate Union implementation here to avoid extra Copy, since both 142 // sNotInOther and otherNotInS are already copies. 143 unique := sNotInOther 144 for e := range otherNotInS.set { 145 unique.set[e] = present{} 146 } 147 return unique 148} 149 150// Equal returns true if the receiver and the argument StringSet contain 151// exactly the same elements. 152func (s *StringSet) Equal(other *StringSet) bool { 153 if s == nil || other == nil { 154 return s == nil && other == nil 155 } 156 157 // Two sets of different length cannot have the exact same unique elements. 158 if len(s.set) != len(other.set) { 159 return false 160 } 161 162 // Only one loop is needed. If the two sets are known to be of equal 163 // length, then the two sets are equal only if exactly all of the elements 164 // in the first set are found in the second. 165 for e := range s.set { 166 if _, ok := other.set[e]; !ok { 167 return false 168 } 169 } 170 171 return true 172} 173 174// Union returns a new StringSet containing the union of the receiver and 175// argument StringSets. Returns a copy of the receiver if the argument is nil. 176func (s *StringSet) Union(other *StringSet) *StringSet { 177 union := s.Copy() 178 if other != nil { 179 for e := range other.set { 180 union.set[e] = present{} 181 } 182 } 183 return union 184} 185 186// Contains returns true if element is in the StringSet. 187func (s *StringSet) Contains(element string) bool { 188 _, in := s.set[element] 189 return in 190} 191 192// Len returns the number of unique elements in the StringSet. 193func (s *StringSet) Len() int { 194 return len(s.set) 195} 196 197// Empty returns true if the receiver is the empty set. 198func (s *StringSet) Empty() bool { 199 return len(s.set) == 0 200} 201 202// Elements returns a []string of the elements in the StringSet, in no 203// particular (or consistent) order. 204func (s *StringSet) Elements() []string { 205 elements := []string{} // Return at least an empty slice rather than nil. 206 for e := range s.set { 207 elements = append(elements, e) 208 } 209 return elements 210} 211 212// Sorted returns a sorted []string of the elements in the StringSet. 213func (s *StringSet) Sorted() []string { 214 elements := s.Elements() 215 sort.Strings(elements) 216 return elements 217} 218 219// String formats the StringSet elements as sorted strings, representing them 220// in "array initializer" syntax. 221func (s *StringSet) String() string { 222 elements := s.Sorted() 223 var quoted []string 224 for _, e := range elements { 225 quoted = append(quoted, fmt.Sprintf("%q", e)) 226 } 227 return fmt.Sprintf("{%s}", strings.Join(quoted, ", ")) 228} 229