github.com/m4gshm/gollections@v0.0.13-0.20240331203319-a34a86e58a24/README.md (about) 1 # Gollections 2 3 Gollections is set of functions for [slices](#slices), [maps](#maps) and 4 additional implementations of data structures such as [ordered 5 map](#mutable-collections) or [set](#mutable-collections) aimed to 6 reduce boilerplate code. 7 8 Supports Go version 1.21. 9 10 For example, it’s need to group some 11 [users](./internal/examples/boilerplate/user_type.go) by their role 12 names converted to lowercase: 13 14 ``` go 15 var users = []User{ 16 {name: "Bob", age: 26, roles: []Role{{"Admin"}, {"manager"}}}, 17 {name: "Alice", age: 35, roles: []Role{{"Manager"}}}, 18 {name: "Tom", age: 18}, 19 } 20 ``` 21 22 You can make clear code, extensive, but without dependencies: 23 24 ``` go 25 var namesByRole = map[string][]string{} 26 add := func(role string, u User) { 27 namesByRole[role] = append(namesByRole[role], u.Name()) 28 } 29 for _, u := range users { 30 if roles := u.Roles(); len(roles) == 0 { 31 add("", u) 32 } else { 33 for _, r := range roles { 34 add(strings.ToLower(r.Name()), u) 35 } 36 } 37 } 38 //map[:[Tom] admin:[Bob] manager:[Bob Alice]] 39 ``` 40 41 Or you can write more compact code using the collections API, like: 42 43 ``` go 44 import "github.com/m4gshm/gollections/slice/convert" 45 import "github.com/m4gshm/gollections/slice/group" 46 47 var namesByRole = group.ByMultipleKeys(users, func(u User) []string { 48 return convert.AndConvert(u.Roles(), Role.Name, strings.ToLower) 49 }, User.Name) 50 // map[:[Tom] admin:[Bob] manager:[Bob Alice]] 51 ``` 52 53 ## Installation 54 55 ``` console 56 go get -u github.com/m4gshm/gollections 57 ``` 58 59 ## Slices 60 61 ``` go 62 data, err := slice.Conv(slice.Of("1", "2", "3", "4", "_", "6"), strconv.Atoi) 63 even := func(i int) bool { return i%2 == 0 } 64 65 result := slice.Reduce(slice.Convert(slice.Filter(data, even), strconv.Itoa), op.Sum) 66 67 assert.ErrorIs(t, err, strconv.ErrSyntax) 68 assert.Equal(t, "24", result) 69 ``` 70 71 In the example is used only small set of slice functions as 72 [slice.Filter](#slicefilter), [slice.Conv](#sliceconv) 73 [slice.Convert](#sliceconvert#), and [slice.Reduce](#slicereduce). More 74 you can look in the [slice](./slice/api.go) package. 75 76 ### Shortcut packages 77 78 ``` go 79 result := sum.Of(filter.AndConvert(data, even, strconv.Itoa)) 80 ``` 81 82 This is a shorter version of the previous example that used short 83 aliases [sum.Of](#sumof) and 84 [filter.AndConvert](#operations-chain-functions). More shortcuts you can 85 find by exploring slices [subpackages](./slice). 86 87 **Be careful** when use several slice functions subsequently like 88 `slice.Filter(slice.Convert(…))`. This can lead to unnecessary RAM 89 consumption. Consider 90 [loop](#loop-kvloop-and-breakable-versions-breakloop-breakkvloop) 91 instead of slice API. 92 93 ### Main slice functions 94 95 #### Instantiators 96 97 ##### slice.Of 98 99 ``` go 100 var s = slice.Of(1, 3, -1, 2, 0) //[]int{1, 3, -1, 2, 0} 101 ``` 102 103 ##### range\_.Of 104 105 ``` go 106 import "github.com/m4gshm/gollections/slice/range_" 107 108 var increasing = range_.Of(-1, 3) //[]int{-1, 0, 1, 2} 109 var decreasing = range_.Of('e', 'a') //[]rune{'e', 'd', 'c', 'b'} 110 var nothing = range_.Of(1, 1) //nil 111 ``` 112 113 ##### range\_.Closed 114 115 ``` go 116 var increasing = range_.Closed(-1, 3) //[]int{-1, 0, 1, 2, 3} 117 var decreasing = range_.Closed('e', 'a') //[]rune{'e', 'd', 'c', 'b', 'a'} 118 var one = range_.Closed(1, 1) //[]int{1} 119 ``` 120 121 #### Sorters 122 123 ##### sort.Asc, sort.Desc 124 125 ``` go 126 // sorting in-place API 127 import "github.com/m4gshm/gollections/slice/sort" 128 129 var ascendingSorted = sort.Asc([]int{1, 3, -1, 2, 0}) //[]int{-1, 0, 1, 2, 3} 130 var descendingSorted = sort.Desc([]int{1, 3, -1, 2, 0}) //[]int{3, 2, 1, 0, -1} 131 ``` 132 133 ##### sort.By, sort.ByDesc 134 135 ``` go 136 // sorting copied slice API does not change the original slice 137 import "github.com/m4gshm/gollections/slice/clone/sort" 138 139 // see the User structure above 140 var users = []User{ 141 {name: "Bob", age: 26}, 142 {name: "Alice", age: 35}, 143 {name: "Tom", age: 18}, 144 {name: "Chris", age: 41}, 145 } 146 147 var byName = sort.By(users, User.Name) 148 //[{Alice 35 []} {Bob 26 []} {Chris 41 []} {Tom 18 []}] 149 150 var byAgeReverse = sort.DescBy(users, User.Age) 151 //[{Chris 41 []} {Alice 35 []} {Bob 26 []} {Tom 18 []}] 152 ``` 153 154 #### To map converters 155 156 ##### group.Of 157 158 ``` go 159 import "github.com/m4gshm/gollections/convert/as" 160 import "github.com/m4gshm/gollections/expr/use" 161 import "github.com/m4gshm/gollections/slice/group" 162 163 var ageGroups = group.Of(users, func(u User) string { 164 return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30") 165 }, as.Is) 166 167 //map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]] 168 ``` 169 170 ##### group.ByMultipleKeys 171 172 ``` go 173 import "github.com/m4gshm/gollections/slice/convert" 174 import "github.com/m4gshm/gollections/slice/group" 175 176 var namesByRole = group.ByMultipleKeys(users, func(u User) []string { 177 return convert.AndConvert(u.Roles(), Role.Name, strings.ToLower) 178 }, User.Name) 179 // map[:[Tom] admin:[Bob] manager:[Bob Alice]] 180 ``` 181 182 ##### slice.ToMap, slice.ToMapResolv 183 184 ``` go 185 import ( 186 "github.com/m4gshm/gollections/map_/resolv" 187 "github.com/m4gshm/gollections/op" 188 "github.com/m4gshm/gollections/slice" 189 ) 190 191 var ageGroupedSortedNames map[string][]string 192 193 ageGroupedSortedNames = slice.ToMapResolv(users, func(u User) string { 194 return op.IfElse(u.age <= 30, "<=30", ">30") 195 }, User.Name, resolv.SortedSlice) 196 197 //map[<=30:[Bob Tom] >30:[Alice Chris]] 198 ``` 199 200 #### Reducers 201 202 ##### sum.Of 203 204 ``` go 205 import "github.com/m4gshm/gollections/op/sum" 206 207 var sum = sum.Of(1, 2, 3, 4, 5, 6) //21 208 ``` 209 210 ##### slice.Reduce 211 212 ``` go 213 var sum = slice.Reduce([]int{1, 2, 3, 4, 5, 6}, func(i1, i2 int) int { return i1 + i2 }) 214 //21 215 ``` 216 217 ##### slice.First 218 219 ``` go 220 import "github.com/m4gshm/gollections/predicate/more" 221 import "github.com/m4gshm/gollections/slice" 222 223 result, ok := slice.First([]int{1, 3, 5, 7, 9, 11}, more.Than(5)) //7, true 224 ``` 225 226 ##### slice.Last 227 228 ``` go 229 import "github.com/m4gshm/gollections/predicate/less" 230 import "github.com/m4gshm/gollections/slice" 231 232 result, ok := slice.Last([]int{1, 3, 5, 7, 9, 11}, less.Than(9)) //7, true 233 ``` 234 235 #### Converters 236 237 ##### slice.Convert 238 239 ``` go 240 var s []string = slice.Convert([]int{1, 3, 5, 7, 9, 11}, strconv.Itoa) 241 //[]string{"1", "3", "5", "7", "9", "11"} 242 ``` 243 244 ##### slice.Conv 245 246 ``` go 247 result, err := slice.Conv(slice.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi) 248 //[]int{1, 3, 5}, ErrSyntax 249 ``` 250 251 ##### slice.Filter 252 253 ``` go 254 import "github.com/m4gshm/gollections/predicate/exclude" 255 import "github.com/m4gshm/gollections/predicate/one" 256 import "github.com/m4gshm/gollections/slice" 257 258 var f1 = slice.Filter([]int{1, 3, 5, 7, 9, 11}, one.Of(1, 7).Or(one.Of(11))) //[]int{1, 7, 11} 259 var f2 = slice.Filter([]int{1, 3, 5, 7, 9, 11}, exclude.All(1, 7, 11)) //[]int{3, 5, 9} 260 ``` 261 262 ##### slice.Flat 263 264 ``` go 265 import "github.com/m4gshm/gollections/convert/as" 266 import "github.com/m4gshm/gollections/slice" 267 268 var i []int = slice.Flat([][]int{{1, 2, 3}, {4}, {5, 6}}, as.Is) 269 //[]int{1, 2, 3, 4, 5, 6} 270 ``` 271 272 #### Operations chain functions 273 274 - convert.AndReduce, conv.AndReduce 275 276 - convert.AndFilter 277 278 - filter.AndConvert 279 280 These functions combine converters, filters and reducers. 281 282 ## Maps 283 284 ### Main map functions 285 286 #### Instantiators 287 288 ##### clone.Of 289 290 ``` go 291 import "github.com/m4gshm/gollections/map_/clone" 292 293 var bob = map[string]string{"name": "Bob"} 294 var tom = map[string]string{"name": "Tom"} 295 296 var employers = map[string]map[string]string{ 297 "devops": bob, 298 "jun": tom, 299 } 300 301 copy := clone.Of(employers) 302 delete(copy, "jun") 303 bob["name"] = "Superbob" 304 305 fmt.Printf("%v\n", employers) //map[devops:map[name:Superbob] jun:map[name:Tom]] 306 fmt.Printf("%v\n", copy) //map[devops:map[name:Superbob]] 307 ``` 308 309 ##### clone.Deep 310 311 ``` go 312 import "github.com/m4gshm/gollections/map_/clone" 313 314 var bob = map[string]string{"name": "Bob"} 315 var tom = map[string]string{"name": "Tom"} 316 317 var employers = map[string]map[string]string{ 318 "devops": bob, 319 "jun": tom, 320 } 321 322 copy := clone.Deep(employers, func(employer map[string]string) map[string]string { 323 return clone.Of(employer) 324 }) 325 delete(copy, "jun") 326 bob["name"] = "Superbob" 327 328 fmt.Printf("%v\n", employers) //map[devops:map[name:Superbob] jun:map[name:Tom]] 329 fmt.Printf("%v\n", copy) //map[devops:map[name:Bob]] 330 ``` 331 332 #### Keys, values exrtractors 333 334 ##### map\_.Keys, map\_.Values 335 336 ``` go 337 var employers = map[string]map[string]string{ 338 "devops": {"name": "Bob"}, 339 "jun": {"name": "Tom"}, 340 } 341 342 keys := map_.Keys(employers) //[devops jun] 343 values := map_.Values(employers) //[map[name:Bob] map[name:Tom]] 344 ``` 345 346 #### Converters 347 348 ##### map\_.ConvertKeys 349 350 ``` go 351 var keys = map_.ConvertKeys(employers, func(title string) string { 352 return string([]rune(title)[0]) 353 }) 354 //map[d:map[name:Bob] j:map[name:Tom]] 355 ``` 356 357 ##### map\_.ConvertValues 358 359 ``` go 360 var vals = map_.ConvertValues(employers, func(employer map[string]string) string { 361 return employer["name"] 362 }) 363 //map[devops:Bob jun:Tom] 364 ``` 365 366 ##### map\_.Convert 367 368 ``` go 369 var all = map_.Convert(employers, func(title string, employer map[string]string) (string, string) { 370 return string([]rune(title)[0]), employer["name"] 371 }) 372 //map[d:Bob j:Tom] 373 ``` 374 375 ##### map\_.Conv 376 377 ``` go 378 var all, err = map_.Conv(employers, func(title string, employer map[string]string) (string, string, error) { 379 return string([]rune(title)[0]), employer["name"], nil 380 }) 381 //map[d:Bob j:Tom], nil 382 ``` 383 384 ##### map\_.ToSlice 385 386 ``` go 387 var users = map_.ToSlice(employers, func(title string, employer map[string]string) User { 388 return User{name: employer["name"], roles: []Role{{name: title}}} 389 }) 390 //[{name:Bob age:0 roles:[{name:devops}]} {name:Tom age:0 roles:[{name:jun}]}] 391 ``` 392 393 ## [loop](./loop/api.go), [kv/loop](./kv/loop/api.go) and breakable versions [break/loop](./break/loop/api.go), [break/kv/loop](./break/kv/loop/api.go) 394 395 Low-level API for iteration based on next functions: 396 397 ``` go 398 type ( 399 Loop[T any] func() (element T, ok bool) 400 KVLoop[K, V any] func() (key K, value V, ok bool) 401 BreakLoop[T any] func() (element T, ok bool, err error) 402 BreakKVLoop[K, V any] func() (key K, value V, ok bool, err error) 403 ) 404 ``` 405 406 The `Loop` function retrieves a next element from a dataset and returns 407 `ok==true` if successful. 408 The `KVLoop` behaves similar but returns a key/value pair. 409 410 ``` go 411 even := func(i int) bool { return i%2 == 0 } 412 stringSeq := loop.Convert(loop.Filter(loop.Of(1, 2, 3, 4), even), strconv.Itoa) 413 414 assert.Equal(t, []string{"2", "4"}, stringSeq.Slice()) 415 ``` 416 417 `BreakLoop` and `BreakKVLoop` are used for sources that can issue an 418 error. 419 420 ``` go 421 intSeq := loop.Conv(loop.Of("1", "2", "3", "ddd4", "5"), strconv.Atoi) 422 ints, err := loop.Slice(intSeq) 423 424 assert.Equal(t, []int{1, 2, 3}, ints) 425 assert.ErrorContains(t, err, "invalid syntax") 426 ``` 427 428 The API in most cases is similar to the [slice](./slice/api.go) API but 429 with delayed computation which means that the methods don’t compute a 430 result but only return a loop provider. The loop provider is type with a 431 `Next` method that returns a next processed element. 432 433 ### Main loop functions 434 435 #### Instantiators 436 437 ##### loop.Of, loop.S 438 439 ``` go 440 import "github.com/m4gshm/gollections/loop" 441 442 var ( 443 ints = loop.Of(1, 2, 3) 444 strings = loop.S([]string{"a", "b", "c"}) 445 ) 446 ``` 447 448 ##### range\_.Of 449 450 ``` go 451 import "github.com/m4gshm/gollections/loop/range_" 452 453 var increasing = range_.Of(-1, 3).Slice() //[]int{-1, 0, 1, 2} 454 var decreasing = range_.Of('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b'} 455 var nothing = range_.Of(1, 1).Slice() //nil 456 ``` 457 458 ##### range\_.Closed 459 460 ``` go 461 var increasing = range_.Closed(-1, 3).Slice() //[]int{-1, 0, 1, 2, 3} 462 var decreasing = range_.Closed('e', 'a').Slice() //[]rune{'e', 'd', 'c', 'b', 'a'} 463 var one = range_.Closed(1, 1).Slice() //[]int{1} 464 ``` 465 466 #### To map converters 467 468 ##### group.Of 469 470 ``` go 471 import "github.com/m4gshm/gollections/convert/as" 472 import "github.com/m4gshm/gollections/expr/use" 473 import "github.com/m4gshm/gollections/loop/group" 474 475 var ageGroups = group.Of(users, func(u User) string { 476 return use.If(u.age <= 20, "<=20").If(u.age <= 30, "<=30").Else(">30") 477 }, as.Is) 478 479 //map[<=20:[{Tom 18 []}] <=30:[{Bob 26 []}] >30:[{Alice 35 []} {Chris 41 []}]] 480 ``` 481 482 ##### loop.ToMap, loop.ToMapResolv 483 484 ``` go 485 import ( 486 "github.com/m4gshm/gollections/map_/resolv" 487 "github.com/m4gshm/gollections/op" 488 "github.com/m4gshm/gollections/loop" 489 ) 490 491 var ageGroupedSortedNames map[string][]string 492 493 ageGroupedSortedNames = loop.ToMapResolv(loop.Of(users...), func(u User) string { 494 return op.IfElse(u.age <= 30, "<=30", ">30") 495 }, User.Name, resolv.SortedSlice) 496 497 //map[<=30:[Bob Tom] >30:[Alice Chris]] 498 ``` 499 500 #### Reducers 501 502 ##### sum.Of 503 504 ``` go 505 import "github.com/m4gshm/gollections/op/sum" 506 507 var sum = sum.Of(loop.Of(1, 2, 3, 4, 5, 6)) //21 508 ``` 509 510 ##### loop.Reduce 511 512 ``` go 513 var sum = loop.Reduce(loop.Of(1, 2, 3, 4, 5, 6), func(i1, i2 int) int { return i1 + i2 }) 514 //21 515 ``` 516 517 ##### loop.First 518 519 ``` go 520 import "github.com/m4gshm/gollections/predicate/more" 521 import "github.com/m4gshm/gollections/loop" 522 523 result, ok := loop.First(loop.Of(1, 3, 5, 7, 9, 11), more.Than(5)) //7, true 524 ``` 525 526 #### Converters 527 528 ##### loop.Convert 529 530 ``` go 531 var s []string = loop.Convert(loop.Of(1, 3, 5, 7, 9, 11), strconv.Itoa).Slice() 532 //[]string{"1", "3", "5", "7", "9", "11"} 533 ``` 534 535 ##### loop.Conv 536 537 ``` go 538 result, err := loop.Conv(loop.Of("1", "3", "5", "_7", "9", "11"), strconv.Atoi).Slice() 539 //[]int{1, 3, 5}, ErrSyntax 540 ``` 541 542 ##### loop.Filter 543 544 ``` go 545 import "github.com/m4gshm/gollections/predicate/exclude" 546 import "github.com/m4gshm/gollections/predicate/one" 547 import "github.com/m4gshm/gollections/loop" 548 549 var f1 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), one.Of(1, 7).Or(one.Of(11))).Slice() //[]int{1, 7, 11} 550 var f2 = loop.Filter(loop.Of(1, 3, 5, 7, 9, 11), exclude.All(1, 7, 11)).Slice() //[]int{3, 5, 9} 551 ``` 552 553 ##### loop.Flat 554 555 ``` go 556 import "github.com/m4gshm/gollections/convert/as" 557 import "github.com/m4gshm/gollections/loop" 558 559 var i []int = loop.Flat(loop.Of([][]int{{1, 2, 3}, {4}, {5, 6}}...), as.Is).Slice() 560 //[]int{1, 2, 3, 4, 5, 6} 561 ``` 562 563 #### Operations chain functions 564 565 - convert.AndReduce, conv.AndReduce 566 567 - convert.AndFilter 568 569 - filter.AndConvert 570 571 These functions combine converters, filters and reducers. 572 573 ### Iterating over loops 574 575 - (only for go 1.22) Using rangefunc `All` like: 576 577 ``` go 578 for i := range range_.Of(0, 100).All { 579 doOp(i) 580 } 581 ``` 582 583 don’t forget exec `go env -w GOEXPERIMENT=rangefunc` before compile. 584 585 - Using `for` statement like: 586 587 ``` go 588 next := range_.Of(0, 100) 589 for i, ok := next(); ok; i, ok = next() { 590 doOp(i) 591 } 592 ``` 593 594 - or 595 596 ``` go 597 for next, i, ok := range_.Of(0, 100).Crank(); ok; i, ok = next() { 598 doOp(i) 599 } 600 ``` 601 602 - `ForEach` method 603 604 ``` go 605 range_.Of(0, 100).ForEach(doOp) 606 ``` 607 608 - or `For` method that can be aborted by returning `Break` for expected 609 completion, or another error otherwise. 610 611 ``` go 612 range_.Of(0, 100).For(func(i int) error { 613 if i > 22 { 614 return loop.Break 615 } 616 doOp(i) 617 return loop.Continue 618 }) 619 ``` 620 621 ## Data structures 622 623 ### [mutable](./collection/mutable/api.go) and [immutable](./collection/immutable/api.go) collections 624 625 Provides implelentations of [Vector](./collection/iface.go#L25), 626 [Set](./collection/iface.go#L35) and [Map](./collection/iface.go#L41). 627 628 Mutables support content appending, updating and deleting (the ordered 629 map implementation is not supported delete operations). 630 Immutables are read-only datasets. 631 632 Detailed description of implementations [below](#mutable-collections). 633 634 ## Additional API 635 636 ### [predicate](./predicate/api.go) and breakable [break/predicate](./predicate/api.go) 637 638 Provides predicate builder api that used for filtering collection 639 elements. 640 641 ``` go 642 import "github.com/m4gshm/gollections/predicate/where" 643 import "github.com/m4gshm/gollections/slice" 644 645 bob, _ := slice.First(users, where.Eq(User.Name, "Bob")) 646 ``` 647 648 It is used for computations where an error may occur. 649 650 ``` go 651 intSeq := loop.Conv(loop.Of("1", "2", "3", "ddd4", "5"), strconv.Atoi) 652 ints, err := loop.Slice(intSeq) 653 654 assert.Equal(t, []int{1, 2, 3}, ints) 655 assert.ErrorContains(t, err, "invalid syntax") 656 ``` 657 658 ### Expressions: [use.If](./expr/use/api.go), [get.If](./expr/get/api.go), [first.Of](#firstof), [last.Of](#lastof) 659 660 Aimed to evaluate a value using conditions. May cause to make code 661 shorter by not in all cases. 662 As example: 663 664 ``` go 665 import "github.com/m4gshm/gollections/expr/use" 666 667 user := User{name: "Bob", surname: "Smith"} 668 669 fullName := use.If(len(user.surname) == 0, user.name).If(len(user.name) == 0, user.surname). 670 ElseGet(func() string { return user.name + " " + user.surname }) 671 672 assert.Equal(t, "Bob Smith", fullName) 673 ``` 674 675 instead of: 676 677 ``` go 678 user := User{name: "Bob", surname: "Smith"} 679 680 fullName := "" 681 if len(user.surname) == 0 { 682 fullName = user.name 683 } else if len(user.name) == 0 { 684 fullName = user.surname 685 } else { 686 fullName = user.name + " " + user.surname 687 } 688 689 assert.Equal(t, "Bob Smith", fullName) 690 ``` 691 692 #### first.Of 693 694 ``` go 695 import "github.com/m4gshm/gollections/expr/first" 696 import "github.com/m4gshm/gollections/predicate/more" 697 698 result, ok := first.Of(1, 3, 5, 7, 9, 11).By(more.Than(5)) //7, true 699 ``` 700 701 #### last.Of 702 703 ``` go 704 import "github.com/m4gshm/gollections/expr/last" 705 import "github.com/m4gshm/gollections/predicate/less" 706 707 result, ok := last.Of(1, 3, 5, 7, 9, 11).By(less.Than(9)) //7, true 708 ``` 709 710 ## Mutable collections 711 712 Supports write operations (append, delete, replace). 713 714 - [Vector](./collection/mutable/vector/api.go) - the simplest based on 715 built-in slice collection. 716 717 ``` go 718 _ *mutable.Vector[int] = vector.Of(1, 2, 3) 719 _ *mutable.Vector[int] = &mutable.Vector[int]{} 720 ``` 721 722 - [Set](./collection/mutable/set/api.go) - collection of unique items, 723 prevents duplicates. 724 725 ``` go 726 _ *mutable.Set[int] = set.Of(1, 2, 3) 727 _ *mutable.Set[int] = &mutable.Set[int]{} 728 ``` 729 730 - [Map](./collection/mutable/map_/api.go) - built-in map wrapper that 731 supports [stream functions](#stream-functions). 732 733 ``` go 734 _ *mutable.Map[int, string] = map_.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3")) 735 _ *mutable.Map[int, string] = mutable.NewMapOf(map[int]string{1: "2", 2: "2", 3: "3"}) 736 ``` 737 738 - [OrderedSet](./collection/mutable/oset/api.go) - collection of unique 739 items, prevents duplicates, provides iteration in order of addition. 740 741 ``` go 742 _ *ordered.Set[int] = set.Of(1, 2, 3) 743 _ *ordered.Set[int] = &ordered.Set[int]{} 744 ``` 745 746 - [OrderedMap](./collection/mutable/omap/api.go) - same as the Map, but 747 supports iteration in the order in which elements are added. 748 749 ``` go 750 _ *ordered.Map[int, string] = map_.Of(k.V(1, "1"), k.V(2, "2"), k.V(3, "3")) 751 _ *ordered.Map[int, string] = ordered.NewMapOf( 752 /*order */ []int{3, 1, 2}, 753 /*uniques*/ map[int]string{1: "2", 2: "2", 3: "3"}, 754 ) 755 ``` 756 757 ### Immutable collections 758 759 The same underlying interfaces but for read-only use cases. 760 761 ### Iterating over collections 762 763 - (only for go 1.22) Using rangefunc `All` like: 764 765 ``` go 766 uniques := set.From(range_.Of(0, 100)) 767 for i := range uniques.All { 768 doOp(i) 769 } 770 ``` 771 772 - Using `for` statement like: 773 774 ``` go 775 uniques := set.From(range_.Of(0, 100)) 776 next := uniques.Loop() 777 for i, ok := next(); ok; i, ok = next() { 778 doOp(i) 779 } 780 ``` 781 782 - or 783 784 ``` go 785 uniques := set.From(range_.Of(0, 100)) 786 for iter, i, ok := uniques.First(); ok; i, ok = iter.Next() { 787 doOp(i) 788 } 789 ``` 790 791 - `ForEach` method 792 793 ``` go 794 uniques := set.From(range_.Of(0, 100)) 795 uniques.ForEach(doOp) 796 ``` 797 798 - or `For` method that can be aborted by returning `Break` for expected 799 completion, or another error otherwise. 800 801 ``` go 802 uniques := set.From(range_.Of(0, 100)) 803 uniques.For(func(i int) error { 804 if i > 22 { 805 return loop.Break 806 } 807 doOp(i) 808 return loop.Continue 809 }) 810 ```