|
| 1 | +# sets |
| 2 | + |
| 3 | +[](https://pkg.go.dev/github.com/kkhmel/sets) |
| 4 | +[](https://github.com/kkhmel/sets/releases) |
| 5 | +[](https://goreportcard.com/report/github.com/kkhmel/sets) |
| 6 | +[](https://github.com/kkhmel/sets/actions) |
| 7 | +[](https://codecov.io/gh/kkhmel/sets/branch/main) |
| 8 | +[](LICENSE) |
| 9 | + |
| 10 | +A high-performance, idiomatic Go library for set operations that follows the same design principles as the standard library's [`maps`](https://pkg.go.dev/maps) and [`slices`](https://pkg.go.dev/slices) packages. |
| 11 | + |
| 12 | +## Design Philosophy: Idiomatic Go |
| 13 | + |
| 14 | +This library combines **idiomatic Go design** with **practical functionality** that developers need in real-world projects. |
| 15 | + |
| 16 | +**Native map at the core.** `Set[T]` is a **type alias** for `map[T]struct{}`, not a wrapper type. All native Go map operations work out of the box: `len()`, `clear()`, `delete()`, `for...range`. |
| 17 | + |
| 18 | +**Follows standard library conventions.** The API mirrors the design of [`maps`](https://pkg.go.dev/maps) and [`slices`](https://pkg.go.dev/slices) — composable functions that each do one thing well, consistent naming, variadic parameters where they make sense. No method chaining or fluent APIs. |
| 19 | + |
| 20 | +**Comprehensive.** ~30 functions covering set theory, functional programming, and iterators. |
| 21 | + |
| 22 | +**Robust.** Read-only functions gracefully handle nil sets, consistent with how the standard library's `maps` and `slices` packages treat nil inputs. Pre-allocated capacity for optimal performance. |
| 23 | + |
| 24 | +**Zero dependencies.** Pure Go standard library only — no external dependencies to manage or security vulnerabilities to track. |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## Installation |
| 29 | + |
| 30 | +```bash |
| 31 | +go get github.com/kkhmel/sets |
| 32 | +``` |
| 33 | + |
| 34 | +Requires Go 1.23+. |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +## Usage |
| 39 | + |
| 40 | +### Quick Start |
| 41 | + |
| 42 | +```go |
| 43 | +package main |
| 44 | + |
| 45 | +import ( |
| 46 | + "fmt" |
| 47 | + "github.com/kkhmel/sets" |
| 48 | +) |
| 49 | + |
| 50 | +func main() { |
| 51 | + // Create sets of user permissions |
| 52 | + admin := sets.From("read", "write", "delete", "admin") |
| 53 | + editor := sets.From("read", "write") |
| 54 | + viewer := sets.From("read") |
| 55 | + |
| 56 | + // Check permissions |
| 57 | + if sets.ContainsAll(admin, "delete", "admin") { |
| 58 | + fmt.Println("Admin has delete and admin privileges") // Output: Admin has delete and admin privileges |
| 59 | + } |
| 60 | + |
| 61 | + // Combine all unique permissions |
| 62 | + allPerms := sets.Union(admin, editor, viewer) |
| 63 | + fmt.Println("All permissions:", allPerms) // Output: All permissions: {admin, delete, read, write} |
| 64 | + |
| 65 | + // Find admin-exclusive permissions |
| 66 | + exclusive := sets.Difference(admin, editor, viewer) |
| 67 | + fmt.Println("Admin-only:", exclusive) // Output: Admin-only: {admin, delete} |
| 68 | + |
| 69 | + // Check subset relationships |
| 70 | + if sets.Subset(viewer, editor) { |
| 71 | + fmt.Println("Viewers are a subset of editors") // Output: Viewers are a subset of editors |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### Guideline |
| 77 | + |
| 78 | +Use builtin `len()` for size, `clear()` for clearing, and `range` for iteration. For all other operations, use `sets` package functions for consistency and cleaner code. |
| 79 | + |
| 80 | +```go |
| 81 | +s := sets.From("a", "b", "c") |
| 82 | + |
| 83 | +// Use native operations for these common cases |
| 84 | +count := len(s) // Get size |
| 85 | +clear(s) // Clear all elements |
| 86 | +for e := range s { // Iterate over elements |
| 87 | + ... |
| 88 | +} |
| 89 | + |
| 90 | +// Use sets package functions for everything else -- cleaner and more consistent |
| 91 | +items := []string{"d", "e"} |
| 92 | +sets.Insert(s, items...) // Instead of: for _, e := range items { s[e] = struct{}{} } |
| 93 | +sets.Delete(s, items...) // Instead of: for _, e := range items { delete(s, e) } |
| 94 | +if sets.Contains(s, "x") { ... } // Instead of: if _, ok := s["x"]; ok { ... } |
| 95 | +if sets.ContainsAny(s, items...) { ... } // Instead of: manual loop with checks |
| 96 | +``` |
| 97 | + |
| 98 | +But since `Set[T]` is a type alias for `map[T]struct{}`, you have full access to native Go map operations and the [`maps`](https://pkg.go.dev/maps) package. |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +## Performance |
| 103 | + |
| 104 | +The library is designed for maximum performance through several key principles: |
| 105 | + |
| 106 | +**Core Performance Principles:** |
| 107 | + |
| 108 | +- **Zero-cost abstraction:** Leverages Go's highly-optimized map implementation directly |
| 109 | +- **Zero-byte values:** Uses `struct{}` as map value (0 bytes per element) for minimal memory footprint |
| 110 | +- **Smart pre-allocation:** Set operations pre-allocate capacity based on input sizes to minimize reallocation |
| 111 | +- **Inlining-friendly:** Simple functions are designed to be inlined by the compiler |
| 112 | + |
| 113 | +**Complexity Information:** |
| 114 | +Detailed time and space complexity for each function is documented in the [API documentation](https://pkg.go.dev/github.com/kkhmel/sets). |
| 115 | + |
| 116 | +**Note:** Call `sets.Clone()` on results to reclaim excess memory if needed. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## Thread Safety |
| 121 | + |
| 122 | +- **Safe for concurrent reads** — Multiple goroutines can safely read from a set simultaneously |
| 123 | +- **Requires synchronization for writes** — Use `sync.RWMutex` or `sync.Mutex` when modifying sets concurrently |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Contributing |
| 128 | + |
| 129 | +Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and contribute to the code. |
0 commit comments