github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/set/strset.go (about) 1 // Copyright 2019 Dolthub, 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. 14 15 package set 16 17 import ( 18 "sort" 19 "strings" 20 21 "github.com/dolthub/dolt/go/libraries/utils/funcitr" 22 ) 23 24 // StrSet is a simple set implementation providing standard set operations for strings. 25 type StrSet struct { 26 items map[string]bool 27 caseSensitive bool 28 } 29 30 // NewStrSet creates a set from a list of strings 31 func newStrSet(items []string, caseSensitive bool) *StrSet { 32 s := &StrSet{make(map[string]bool, len(items)), caseSensitive} 33 34 for _, item := range items { 35 s.items[item] = true 36 } 37 38 return s 39 } 40 41 func NewEmptyStrSet() *StrSet { 42 return newStrSet(nil, true) 43 } 44 45 func NewStrSet(items []string) *StrSet { 46 return newStrSet(items, true) 47 } 48 49 func NewCaseInsensitiveStrSet(items []string) *StrSet { 50 lwrStrs := funcitr.MapStrings(items, strings.ToLower) 51 return newStrSet(lwrStrs, false) 52 } 53 54 // Add adds new items to the set 55 func (s *StrSet) Add(items ...string) { 56 for _, item := range items { 57 if !s.caseSensitive { 58 item = strings.ToLower(item) 59 } 60 s.items[item] = true 61 } 62 } 63 64 // Remove removes existing items from the set 65 func (s *StrSet) Remove(items ...string) { 66 for _, item := range items { 67 if !s.caseSensitive { 68 item = strings.ToLower(item) 69 } 70 71 delete(s.items, item) 72 } 73 } 74 75 // Contains returns true if the item being checked is already in the set. 76 func (s *StrSet) Contains(item string) bool { 77 if s == nil { 78 return false 79 } 80 if !s.caseSensitive { 81 item = strings.ToLower(item) 82 } 83 84 _, present := s.items[item] 85 return present 86 } 87 88 // ContainsAll returns true if all the items being checked are already in the set. 89 func (s *StrSet) ContainsAll(items []string) bool { 90 if s == nil { 91 return false 92 } 93 if !s.caseSensitive { 94 items = funcitr.MapStrings(items, strings.ToLower) 95 } 96 97 for _, item := range items { 98 if _, present := s.items[item]; !present { 99 return false 100 } 101 } 102 103 return true 104 } 105 106 func (s *StrSet) Equals(other *StrSet) bool { 107 // two string sets can be equal even if one is sensitive and the other is insensitive as long al the items are a 108 // case sensitive match. 109 ss := s.AsSlice() 110 os := other.AsSlice() 111 sort.Strings(ss) 112 sort.Strings(os) 113 114 if len(ss) != len(os) { 115 return false 116 } 117 118 for i := range ss { 119 if ss[i] != os[i] { 120 return false 121 } 122 } 123 return true 124 } 125 126 // Size returns the number of unique elements in the set 127 func (s *StrSet) Size() int { 128 if s == nil { 129 return 0 130 } 131 return len(s.items) 132 } 133 134 // AsSlice converts the set to a slice of strings. If this is an insensitive set the resulting slice will be lowercase 135 // regardless of the case that was used when adding the string to the set. 136 func (s *StrSet) AsSlice() []string { 137 if s == nil { 138 return nil 139 } 140 size := len(s.items) 141 sl := make([]string, size) 142 143 i := 0 144 for k := range s.items { 145 sl[i] = k 146 i++ 147 } 148 149 return sl 150 } 151 152 // AsSortedSlice converts the set to a slice of strings. If this is an insensitive set the resulting slice will be lowercase 153 // regardless of the case that was used when adding the string to the set. The slice is sorted in ascending order. 154 func (s *StrSet) AsSortedSlice() []string { 155 if s == nil { 156 return nil 157 } 158 slice := s.AsSlice() 159 sort.Slice(slice, func(i, j int) bool { 160 return slice[i] < slice[j] 161 }) 162 return slice 163 } 164 165 // Iterate accepts a callback which will be called once for each element in the set until all items have been 166 // exhausted or callback returns false. 167 func (s *StrSet) Iterate(callBack func(string) (cont bool)) { 168 if s == nil { 169 return 170 } 171 for k := range s.items { 172 if !callBack(k) { 173 break 174 } 175 } 176 } 177 178 // LeftIntersectionRight takes a slice of strings and returns a slice of strings containing the intersection with the 179 // set, and a slice of strings for the ones missing from the set. 180 func (s *StrSet) LeftIntersectionRight(other *StrSet) (left *StrSet, intersection *StrSet, right *StrSet) { 181 left = NewStrSet(nil) 182 intersection = NewStrSet(nil) 183 right = NewStrSet(nil) 184 185 for os := range other.items { 186 if s.Contains(os) { 187 intersection.Add(os) 188 } else { 189 right.Add(os) 190 } 191 } 192 for ss := range s.items { 193 if !intersection.Contains(ss) { 194 left.Add(ss) 195 } 196 } 197 198 return left, intersection, right 199 } 200 201 // JoinStrings returns the sorted values from the set concatenated with a given sep 202 func (s *StrSet) JoinStrings(sep string) string { 203 strSl := s.AsSlice() 204 sort.Strings(strSl) 205 return strings.Join(strSl, sep) 206 } 207 208 // Unique will return a slice of unique strings given an input slice 209 func Unique(strs []string) []string { 210 return NewStrSet(strs).AsSlice() 211 }