github.com/wangyougui/gf/v2@v2.6.5/container/gset/gset_str_set.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 // 7 8 package gset 9 10 import ( 11 "bytes" 12 "strings" 13 14 "github.com/wangyougui/gf/v2/internal/json" 15 "github.com/wangyougui/gf/v2/internal/rwmutex" 16 "github.com/wangyougui/gf/v2/text/gstr" 17 "github.com/wangyougui/gf/v2/util/gconv" 18 ) 19 20 type StrSet struct { 21 mu rwmutex.RWMutex 22 data map[string]struct{} 23 } 24 25 // NewStrSet create and returns a new set, which contains un-repeated items. 26 // The parameter `safe` is used to specify whether using set in concurrent-safety, 27 // which is false in default. 28 func NewStrSet(safe ...bool) *StrSet { 29 return &StrSet{ 30 mu: rwmutex.Create(safe...), 31 data: make(map[string]struct{}), 32 } 33 } 34 35 // NewStrSetFrom returns a new set from `items`. 36 func NewStrSetFrom(items []string, safe ...bool) *StrSet { 37 m := make(map[string]struct{}) 38 for _, v := range items { 39 m[v] = struct{}{} 40 } 41 return &StrSet{ 42 mu: rwmutex.Create(safe...), 43 data: m, 44 } 45 } 46 47 // Iterator iterates the set readonly with given callback function `f`, 48 // if `f` returns true then continue iterating; or false to stop. 49 func (set *StrSet) Iterator(f func(v string) bool) { 50 set.mu.RLock() 51 defer set.mu.RUnlock() 52 for k := range set.data { 53 if !f(k) { 54 break 55 } 56 } 57 } 58 59 // Add adds one or multiple items to the set. 60 func (set *StrSet) Add(item ...string) { 61 set.mu.Lock() 62 if set.data == nil { 63 set.data = make(map[string]struct{}) 64 } 65 for _, v := range item { 66 set.data[v] = struct{}{} 67 } 68 set.mu.Unlock() 69 } 70 71 // AddIfNotExist checks whether item exists in the set, 72 // it adds the item to set and returns true if it does not exist in the set, 73 // or else it does nothing and returns false. 74 func (set *StrSet) AddIfNotExist(item string) bool { 75 if !set.Contains(item) { 76 set.mu.Lock() 77 defer set.mu.Unlock() 78 if set.data == nil { 79 set.data = make(map[string]struct{}) 80 } 81 if _, ok := set.data[item]; !ok { 82 set.data[item] = struct{}{} 83 return true 84 } 85 } 86 return false 87 } 88 89 // AddIfNotExistFunc checks whether item exists in the set, 90 // it adds the item to set and returns true if it does not exists in the set and 91 // function `f` returns true, or else it does nothing and returns false. 92 // 93 // Note that, the function `f` is executed without writing lock. 94 func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool { 95 if !set.Contains(item) { 96 if f() { 97 set.mu.Lock() 98 defer set.mu.Unlock() 99 if set.data == nil { 100 set.data = make(map[string]struct{}) 101 } 102 if _, ok := set.data[item]; !ok { 103 set.data[item] = struct{}{} 104 return true 105 } 106 } 107 } 108 return false 109 } 110 111 // AddIfNotExistFuncLock checks whether item exists in the set, 112 // it adds the item to set and returns true if it does not exists in the set and 113 // function `f` returns true, or else it does nothing and returns false. 114 // 115 // Note that, the function `f` is executed without writing lock. 116 func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool { 117 if !set.Contains(item) { 118 set.mu.Lock() 119 defer set.mu.Unlock() 120 if set.data == nil { 121 set.data = make(map[string]struct{}) 122 } 123 if f() { 124 if _, ok := set.data[item]; !ok { 125 set.data[item] = struct{}{} 126 return true 127 } 128 } 129 } 130 return false 131 } 132 133 // Contains checks whether the set contains `item`. 134 func (set *StrSet) Contains(item string) bool { 135 var ok bool 136 set.mu.RLock() 137 if set.data != nil { 138 _, ok = set.data[item] 139 } 140 set.mu.RUnlock() 141 return ok 142 } 143 144 // ContainsI checks whether a value exists in the set with case-insensitively. 145 // Note that it internally iterates the whole set to do the comparison with case-insensitively. 146 func (set *StrSet) ContainsI(item string) bool { 147 set.mu.RLock() 148 defer set.mu.RUnlock() 149 for k := range set.data { 150 if strings.EqualFold(k, item) { 151 return true 152 } 153 } 154 return false 155 } 156 157 // Remove deletes `item` from set. 158 func (set *StrSet) Remove(item string) { 159 set.mu.Lock() 160 if set.data != nil { 161 delete(set.data, item) 162 } 163 set.mu.Unlock() 164 } 165 166 // Size returns the size of the set. 167 func (set *StrSet) Size() int { 168 set.mu.RLock() 169 l := len(set.data) 170 set.mu.RUnlock() 171 return l 172 } 173 174 // Clear deletes all items of the set. 175 func (set *StrSet) Clear() { 176 set.mu.Lock() 177 set.data = make(map[string]struct{}) 178 set.mu.Unlock() 179 } 180 181 // Slice returns the an of items of the set as slice. 182 func (set *StrSet) Slice() []string { 183 set.mu.RLock() 184 var ( 185 i = 0 186 ret = make([]string, len(set.data)) 187 ) 188 for item := range set.data { 189 ret[i] = item 190 i++ 191 } 192 193 set.mu.RUnlock() 194 return ret 195 } 196 197 // Join joins items with a string `glue`. 198 func (set *StrSet) Join(glue string) string { 199 set.mu.RLock() 200 defer set.mu.RUnlock() 201 if len(set.data) == 0 { 202 return "" 203 } 204 var ( 205 l = len(set.data) 206 i = 0 207 buffer = bytes.NewBuffer(nil) 208 ) 209 for k := range set.data { 210 buffer.WriteString(k) 211 if i != l-1 { 212 buffer.WriteString(glue) 213 } 214 i++ 215 } 216 return buffer.String() 217 } 218 219 // String returns items as a string, which implements like json.Marshal does. 220 func (set *StrSet) String() string { 221 if set == nil { 222 return "" 223 } 224 set.mu.RLock() 225 defer set.mu.RUnlock() 226 var ( 227 l = len(set.data) 228 i = 0 229 buffer = bytes.NewBuffer(nil) 230 ) 231 buffer.WriteByte('[') 232 for k := range set.data { 233 buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`) 234 if i != l-1 { 235 buffer.WriteByte(',') 236 } 237 i++ 238 } 239 buffer.WriteByte(']') 240 return buffer.String() 241 } 242 243 // LockFunc locks writing with callback function `f`. 244 func (set *StrSet) LockFunc(f func(m map[string]struct{})) { 245 set.mu.Lock() 246 defer set.mu.Unlock() 247 f(set.data) 248 } 249 250 // RLockFunc locks reading with callback function `f`. 251 func (set *StrSet) RLockFunc(f func(m map[string]struct{})) { 252 set.mu.RLock() 253 defer set.mu.RUnlock() 254 f(set.data) 255 } 256 257 // Equal checks whether the two sets equal. 258 func (set *StrSet) Equal(other *StrSet) bool { 259 if set == other { 260 return true 261 } 262 set.mu.RLock() 263 defer set.mu.RUnlock() 264 other.mu.RLock() 265 defer other.mu.RUnlock() 266 if len(set.data) != len(other.data) { 267 return false 268 } 269 for key := range set.data { 270 if _, ok := other.data[key]; !ok { 271 return false 272 } 273 } 274 return true 275 } 276 277 // IsSubsetOf checks whether the current set is a sub-set of `other`. 278 func (set *StrSet) IsSubsetOf(other *StrSet) bool { 279 if set == other { 280 return true 281 } 282 set.mu.RLock() 283 defer set.mu.RUnlock() 284 other.mu.RLock() 285 defer other.mu.RUnlock() 286 for key := range set.data { 287 if _, ok := other.data[key]; !ok { 288 return false 289 } 290 } 291 return true 292 } 293 294 // Union returns a new set which is the union of `set` and `other`. 295 // Which means, all the items in `newSet` are in `set` or in `other`. 296 func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) { 297 newSet = NewStrSet() 298 set.mu.RLock() 299 defer set.mu.RUnlock() 300 for _, other := range others { 301 if set != other { 302 other.mu.RLock() 303 } 304 for k, v := range set.data { 305 newSet.data[k] = v 306 } 307 if set != other { 308 for k, v := range other.data { 309 newSet.data[k] = v 310 } 311 } 312 if set != other { 313 other.mu.RUnlock() 314 } 315 } 316 317 return 318 } 319 320 // Diff returns a new set which is the difference set from `set` to `other`. 321 // Which means, all the items in `newSet` are in `set` but not in `other`. 322 func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) { 323 newSet = NewStrSet() 324 set.mu.RLock() 325 defer set.mu.RUnlock() 326 for _, other := range others { 327 if set == other { 328 continue 329 } 330 other.mu.RLock() 331 for k, v := range set.data { 332 if _, ok := other.data[k]; !ok { 333 newSet.data[k] = v 334 } 335 } 336 other.mu.RUnlock() 337 } 338 return 339 } 340 341 // Intersect returns a new set which is the intersection from `set` to `other`. 342 // Which means, all the items in `newSet` are in `set` and also in `other`. 343 func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) { 344 newSet = NewStrSet() 345 set.mu.RLock() 346 defer set.mu.RUnlock() 347 for _, other := range others { 348 if set != other { 349 other.mu.RLock() 350 } 351 for k, v := range set.data { 352 if _, ok := other.data[k]; ok { 353 newSet.data[k] = v 354 } 355 } 356 if set != other { 357 other.mu.RUnlock() 358 } 359 } 360 return 361 } 362 363 // Complement returns a new set which is the complement from `set` to `full`. 364 // Which means, all the items in `newSet` are in `full` and not in `set`. 365 // 366 // It returns the difference between `full` and `set` 367 // if the given set `full` is not the full set of `set`. 368 func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) { 369 newSet = NewStrSet() 370 set.mu.RLock() 371 defer set.mu.RUnlock() 372 if set != full { 373 full.mu.RLock() 374 defer full.mu.RUnlock() 375 } 376 for k, v := range full.data { 377 if _, ok := set.data[k]; !ok { 378 newSet.data[k] = v 379 } 380 } 381 return 382 } 383 384 // Merge adds items from `others` sets into `set`. 385 func (set *StrSet) Merge(others ...*StrSet) *StrSet { 386 set.mu.Lock() 387 defer set.mu.Unlock() 388 for _, other := range others { 389 if set != other { 390 other.mu.RLock() 391 } 392 for k, v := range other.data { 393 set.data[k] = v 394 } 395 if set != other { 396 other.mu.RUnlock() 397 } 398 } 399 return set 400 } 401 402 // Sum sums items. 403 // Note: The items should be converted to int type, 404 // or you'd get a result that you unexpected. 405 func (set *StrSet) Sum() (sum int) { 406 set.mu.RLock() 407 defer set.mu.RUnlock() 408 for k := range set.data { 409 sum += gconv.Int(k) 410 } 411 return 412 } 413 414 // Pop randomly pops an item from set. 415 func (set *StrSet) Pop() string { 416 set.mu.Lock() 417 defer set.mu.Unlock() 418 for k := range set.data { 419 delete(set.data, k) 420 return k 421 } 422 return "" 423 } 424 425 // Pops randomly pops `size` items from set. 426 // It returns all items if size == -1. 427 func (set *StrSet) Pops(size int) []string { 428 set.mu.Lock() 429 defer set.mu.Unlock() 430 if size > len(set.data) || size == -1 { 431 size = len(set.data) 432 } 433 if size <= 0 { 434 return nil 435 } 436 index := 0 437 array := make([]string, size) 438 for k := range set.data { 439 delete(set.data, k) 440 array[index] = k 441 index++ 442 if index == size { 443 break 444 } 445 } 446 return array 447 } 448 449 // Walk applies a user supplied function `f` to every item of set. 450 func (set *StrSet) Walk(f func(item string) string) *StrSet { 451 set.mu.Lock() 452 defer set.mu.Unlock() 453 m := make(map[string]struct{}, len(set.data)) 454 for k, v := range set.data { 455 m[f(k)] = v 456 } 457 set.data = m 458 return set 459 } 460 461 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 462 func (set StrSet) MarshalJSON() ([]byte, error) { 463 return json.Marshal(set.Slice()) 464 } 465 466 // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. 467 func (set *StrSet) UnmarshalJSON(b []byte) error { 468 set.mu.Lock() 469 defer set.mu.Unlock() 470 if set.data == nil { 471 set.data = make(map[string]struct{}) 472 } 473 var array []string 474 if err := json.UnmarshalUseNumber(b, &array); err != nil { 475 return err 476 } 477 for _, v := range array { 478 set.data[v] = struct{}{} 479 } 480 return nil 481 } 482 483 // UnmarshalValue is an interface implement which sets any type of value for set. 484 func (set *StrSet) UnmarshalValue(value interface{}) (err error) { 485 set.mu.Lock() 486 defer set.mu.Unlock() 487 if set.data == nil { 488 set.data = make(map[string]struct{}) 489 } 490 var array []string 491 switch value.(type) { 492 case string, []byte: 493 err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) 494 default: 495 array = gconv.SliceStr(value) 496 } 497 for _, v := range array { 498 set.data[v] = struct{}{} 499 } 500 return 501 } 502 503 // DeepCopy implements interface for deep copy of current type. 504 func (set *StrSet) DeepCopy() interface{} { 505 if set == nil { 506 return nil 507 } 508 set.mu.RLock() 509 defer set.mu.RUnlock() 510 var ( 511 slice = make([]string, len(set.data)) 512 index = 0 513 ) 514 for k := range set.data { 515 slice[index] = k 516 index++ 517 } 518 return NewStrSetFrom(slice, set.mu.IsSafe()) 519 }