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