How do you implement a set in Go, and what maps package helpers exist?
Question 71MediumGo 1.22 to 1.25
Go has no built-in set type. The idiom is map[T]struct{}. The empty struct takes zero bytes, so values cost nothing, and the type makes the intent clear. map[T]bool uses 1 byte per entry and allows if set[x], but it also allows an ambiguous false value.
type Set[T comparable] map[T]struct{}
func Of[T comparable](items ...T) Set[T] {
s := make(Set[T], len(items))
for _, v := range items {
s[v] = struct{}{}
}
return s
}
func (s Set[T]) Has(v T) bool { _, ok := s[v]; return ok }
func (s Set[T]) Add(v T) { s[v] = struct{}{} }
func (s Set[T]) Intersect(o Set[T]) Set[T] {
small, big := s, o
if len(big) < len(small) {
small, big = big, small // iterate the smaller set
}
out := Set[T]{}
for k := range small {
if big.Has(k) {
out.Add(k)
}
}
return out
}
// Sorted, deterministic listing
func (s Set[T]) Sorted(less func(a, b T) int) []T {
return slices.SortedFunc(maps.Keys(s), less)
}
maps package helpers:
maps.Clonemakes a shallow copy and keeps nil as nil.maps.Copy(dst, src)overwrites any keys that already exist indst.maps.Equalandmaps.EqualFunccompare maps.maps.DeleteFuncdeletes the entries that match a predicate.- Since Go 1.23 there are also
maps.Keys,maps.Valuesandmaps.All(iterators), andmaps.Collectandmaps.Insert.
Gotcha: maps.Clone is shallow. Slice or map values inside it are still shared.
More on Arrays, Slices, Maps & Strings
- Q69How do you build strings efficiently? Compare +, fmt.Sprintf, bytes.Buffer and strings.Builder.
- Q70How do you convert between []byte and string without copying, and what are the risks?
- Q72What does this print before and after Go 1.22? (taking &v and capturing i in loops)
- Q73What does this print? (map of slices built from a shared base)
- Q74What does this print? (range over a nil pointer to an array, and range over an int)
- Q75How do range-over-func iterators work on slices and maps, and what happens if an iterator ignores yield's return value?