Generic set data structure for Go
The setz package provides a Set[T] type with O(1) operations for set membership, union, intersection, and more. Implemented as a type alias of map[T]struct{} for zero overhead.
By Category:
- Construction - New, FromSlice, FromMap
- Basic Operations - Add, Remove, Contains, Pop, Clear
- Set Operations - Union, Intersection, Difference
- Predicates - IsSubset, IsSuperset, IsDisjoint, IsEqual
- Conversion - ToSlice, Copy, Filter, Map
go get github.com/modfin/henry/setzimport "github.com/modfin/henry/setz"
// Create sets
s1 := setz.New(1, 2, 3, 4, 5)
s2 := setz.New(4, 5, 6, 7, 8)
// Set operations
union := s1.Union(s2) // {1, 2, 3, 4, 5, 6, 7, 8}
intersection := s1.Intersection(s2) // {4, 5}
difference := s1.Difference(s2) // {1, 2, 3}
// Check membership
if s1.Contains(3) {
fmt.Println("3 is in the set")
}Create a set from elements.
s := setz.New(1, 2, 3, 4, 5)
// s = Set{1, 2, 3, 4, 5}Create from a slice.
nums := []int{1, 2, 2, 3, 3, 3}
s := setz.FromSlice(nums)
// s = Set{1, 2, 3} (duplicates removed)Create from an existing map.
m := map[string]struct{}{"a": {}, "b": {}}
s := setz.FromMap(m)
// s = Set{"a", "b"}Add elements (mutates).
s := setz.New(1, 2)
s.Add(3, 4, 5)
// s = Set{1, 2, 3, 4, 5}Remove elements (mutates).
s := setz.New(1, 2, 3, 4, 5)
s.Remove(2, 4)
// s = Set{1, 3, 5}Check membership.
s := setz.New(1, 2, 3)
s.Contains(2) // true
s.Contains(5) // falseCheck if all elements are present.
s := setz.New(1, 2, 3, 4, 5)
s.ContainsAll(2, 4) // true
s.ContainsAll(2, 6) // false (6 not in set)Remove and return arbitrary element.
s := setz.New(1, 2, 3)
elem, ok := s.Pop()
// elem = 1 (or 2 or 3), ok = true
// s now has 2 elements
// Empty set
empty := setz.New[int]()
elem, ok := empty.Pop()
// elem = 0 (zero value), ok = falseRemove all elements (mutates).
s := setz.New(1, 2, 3)
s.Clear()
// s = Set{}Get number of elements.
s := setz.New(1, 2, 3)
n := s.Len() // n = 3Check if empty.
s := setz.New[int]()
s.IsEmpty() // trueCombine two sets (new set).
s1 := setz.New(1, 2, 3)
s2 := setz.New(2, 3, 4)
union := s1.Union(s2)
// union = Set{1, 2, 3, 4}Common elements (new set).
s1 := setz.New(1, 2, 3, 4)
s2 := setz.New(3, 4, 5, 6)
common := s1.Intersection(s2)
// common = Set{3, 4}Elements in first but not second (new set).
s1 := setz.New(1, 2, 3, 4)
s2 := setz.New(3, 4, 5, 6)
diff := s1.Difference(s2)
// diff = Set{1, 2}Elements in exactly one set (XOR).
s1 := setz.New(1, 2, 3)
s2 := setz.New(2, 3, 4)
xor := s1.SymmetricDifference(s2)
// xor = Set{1, 4}Check if all elements are in another set.
small := setz.New(1, 2)
large := setz.New(1, 2, 3, 4)
small.IsSubset(large) // true
large.IsSubset(small) // falseCheck if contains all elements of another set.
large := setz.New(1, 2, 3, 4)
small := setz.New(1, 2)
large.IsSuperset(small) // trueSubset but not equal.
a := setz.New(1, 2)
b := setz.New(1, 2, 3)
a.IsProperSubset(b) // true
b.IsProperSubset(a) // falseSuperset but not equal.
a := setz.New(1, 2, 3)
b := setz.New(1, 2)
a.IsProperSuperset(b) // trueNo common elements.
s1 := setz.New(1, 2, 3)
s2 := setz.New(4, 5, 6)
s1.IsDisjoint(s2) // true
s3 := setz.New(3, 4, 5)
s1.IsDisjoint(s3) // false (3 in common)Same elements.
s1 := setz.New(1, 2, 3)
s2 := setz.New(3, 2, 1) // Order doesn't matter
s1.IsEqual(s2) // trueConvert to slice (order not guaranteed).
s := setz.New(1, 2, 3)
slice := s.ToSlice()
// slice = []int{1, 2, 3} (or any order)Create a copy.
s1 := setz.New(1, 2, 3)
s2 := s1.Copy()
// s2 is independent copyFilter elements (new set).
s := setz.New(1, 2, 3, 4, 5, 6)
evens := s.Filter(func(n int) bool {
return n%2 == 0
})
// evens = Set{2, 4, 6}Transform elements (new set).
s := setz.New(1, 2, 3)
doubled := setz.Map(s, func(n int) int {
return n * 2
})
// doubled = Set{2, 4, 6}Union of multiple sets.
s1 := setz.New(1, 2)
s2 := setz.New(2, 3)
s3 := setz.New(3, 4)
combined := setz.Union(s1, s2, s3)
// combined = Set{1, 2, 3, 4}Intersection of multiple sets.
s1 := setz.New(1, 2, 3, 4)
s2 := setz.New(2, 3, 4, 5)
s3 := setz.New(3, 4, 5, 6)
common := setz.Intersection(s1, s2, s3)
// common = Set{3, 4}Difference with multiple sets.
s1 := setz.New(1, 2, 3, 4, 5)
s2 := setz.New(2, 3)
s3 := setz.New(4)
result := setz.Difference(s1, s2, s3)
// result = Set{1, 5}Check membership (functional style).
s := setz.New(1, 2, 3)
setz.Contains(s, 2) // trueConvert to slice.
s := setz.New(1, 2, 3)
slice := setz.ToSlice(s)Check subset relation.
small := setz.New(1, 2)
large := setz.New(1, 2, 3)
setz.IsSubset(small, large) // trueCheck disjointness.
s1 := setz.New(1, 2)
s2 := setz.New(3, 4)
setz.IsDisjoint(s1, s2) // trues := setz.New(1, 2, 3)
fmt.Println(s.String())
// Output: Set[1 2 3] (or any order)
empty := setz.New[int]()
fmt.Println(empty.String())
// Output: Set{}- O(1) operations: Add, Remove, Contains, Len
- O(n) operations: Union, Intersection, ToSlice
- Zero overhead:
Set[T]is justmap[T]struct{} - Memory efficient: Uses empty struct (0 bytes) for values
Use setz when:
- You need O(1) membership testing
- Doing set operations (union, intersection)
- Deduplication is the primary concern
Use slicez set functions when:
- Order matters
- You need indexed access
- Memory is constrained (sets use 2x memory)
// setz version - O(1) lookup
s := setz.New(1, 2, 3, 4, 5)
exists := s.Contains(3)
// slicez version - O(n) lookup
nums := []int{1, 2, 3, 4, 5}
exists := slicez.Contains(nums, 3)