set

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2025 License: MIT Imports: 5 Imported by: 0

README

go-set

Generic set for Golang

Loosely based on the python3 sets operations using generics and iterators.

Uses a map as a synonym of a set. Members of a set must be comparable. Unfortunately this precludes a 'Set of sets' as maps (on which Sets are based) are not comparable.

See https://go.dev/blog/generic-interfaces for a much deeper discussion of Sets and generics.

Documentation

Overview

Package set implements a Set using an underlying map as an aliased type.

Additionally the Set implements methods from the python set type including unions, intersections, differences and symmetric differences.

Data can be fed to a set from an array or slice and additionally from an iterator. The implementation avoids allocations where possible.

Unlike python, sets can only consist of comparable types. This eliminates the possibility of a 'set of sets'.

Similarly to map, sets are not goroutine safe.

This is not production code but simply a demonstration of generics and iterators.

[Python Set]: https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset Frozenset is NOT supported although it would be possible.

This implementation using generics has a few problems. Map keys or sets are 'comparable' (obeys the == and != operations) whereas slices are 'cmp.Ordered' (obeys <, <=, ==, >=, > operations) and this introduces some basic incompatibilities. Attempting to sort a set using slices.Sort from the go slices package will not work as this requires the cmp.Ordered constraint.

A better solution is to implement 'set' as a stripped down version of 'map' in the go source code perhaps.

For a deep discussion of generics see the blog by Axel Wagner

https://go.dev/blog/generic-interfaces

It is tempting to change the comparable constraint to a constraint that EITHER requires 'comparable' OR the presence of an Equal method - something like this:

type Comparer[T any] interface {
	Equal(T) bool
}

type Comparable[T any] interface {
	comparable | Comparer[T]
}

but this will not compile as 'comparable' is disallowed from forming part of a constraint union.

To fix this the various types that make up 'comparable' must be explicitly enumerated in the Comparable type. This is fragile as this may change in subsequent Go versions.

type Comparable[T any] interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
	~float32 | ~float64 |
	~string | Comparer[T]
}

and it is unclear how to generalise this to (say) structs and pointers of any type.

Ref: https://github.com/golang/go/issues/51259

Ref: https://go.dev/blog/comparable

An alternative would be to use:

type Comparable[T any] interface {
	comparable
	Comparer[T]
}

but this makes generic types such as 'string' or 'int' fail because they have no Equal method.

To reiterate the only real solution is to add 'set' as a proper type in the golang source tree.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Equal added in v1.0.0

func Equal[S Set[T], T comparable](s1 S, s2 S) bool

Equal returns true if two sets contain the same items.

Types

type Set

type Set[T comparable] map[T]struct{}

Set is a synonym of a map with no values.

func FromIter

func FromIter[T comparable](items iter.Seq[T]) Set[T]

FromIter creates a new set from an iterator. This is useful for creating a set from a map.

Example
m := map[string]int{"a": 0, "b": 1}
s := set.FromIter(maps.Keys(m))
fmt.Printf(
	"%d %t %t",
	len(s),
	s.Contains("a"),
	s.Contains("b"),
)
Output:

2 true true

func FromSlice

func FromSlice[T comparable](items ...T) Set[T]

FromSlice creates a new set from a slice or array.

Example
a := []string{"a", "b"}
s := set.FromSlice(a...)
fmt.Printf(
	"%d %t %t",
	len(s),
	s.Contains("a"),
	s.Contains("b"),
)
Output:

2 true true
Example (Int)
a := []int{1, 2}
s := set.FromSlice(a...)
fmt.Printf(
	"%d %t %t",
	len(s),
	s.Contains(1),
	s.Contains(2),
)
Output:

2 true true
Example (Struct)
type testStruct struct {
	name  string
	index int
}

oneTestStruct := testStruct{"one", 1}
twoTestStruct := testStruct{"two", 2}

a := []testStruct{oneTestStruct, twoTestStruct}
s := set.FromSlice(a...)
fmt.Printf(
	"%d %t %t",
	len(s),
	s.Contains(oneTestStruct),
	s.Contains(twoTestStruct),
)
Output:

2 true true

func (Set[T]) Add

func (s Set[T]) Add(items ...T)

Add adds one or more items to set.

Example
s := set.FromSlice("a")
s.Add("b", "c")
fmt.Printf(
	"%d %t %t %t",
	len(s),
	s.Contains("a"),
	s.Contains("b"),
	s.Contains("c"),
)
Output:

3 true true true

func (Set[T]) Contains

func (s Set[T]) Contains(item T) bool

Contains returns true if item is present in the set.

func (Set[T]) Difference

func (s Set[T]) Difference(other Set[T]) Set[T]

Difference returns set that consists of items that are in first set and not in second set.

Example
a := []string{"a", "b", "c"}
m := []string{"c", "d", "e"}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.Difference(t)
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains("a"),
	u.Contains("b"),
	u.Contains("c"),
	u.Contains("d"),
	u.Contains("e"),
)
Output:

2 true true false false false

func (Set[T]) Equal added in v1.0.0

func (s Set[T]) Equal(other Set[T]) bool

Equal returns true if sets are equal.

Example
a := []int{1, 2}
b := []int{1, 2}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Equal(t),
)
Output:

true
Example (Not)
a := []int{1, 2}
b := []int{1, 3}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Equal(t),
)
Output:

false
Example (Wronglength)
a := []int{1, 2}
b := []int{1, 2, 3}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Equal(t),
)
Output:

false

func (Set[T]) Intersection

func (s Set[T]) Intersection(other Set[T]) Set[T]

Intersection returns set that consists of items that are in both sets.

Example
a := []string{"a", "b", "c"}
m := []string{"c", "d", "e"}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.Intersection(t)
fmt.Printf("%v", u)
Output:

{c}

func (Set[T]) IntersectionIter

func (s Set[T]) IntersectionIter(items iter.Seq[T]) Set[T]

IntersectionIter returns set that consists of items that are in both set and iterable.

Example
a := []string{"a", "b", "c"}
m := map[string]int{"c": 0, "d": 1, "e": 2}
s := set.FromSlice(a...)
u := s.IntersectionIter(maps.Keys(m))
fmt.Printf("%v", u)
Output:

{c}

func (Set[T]) Iter

func (s Set[T]) Iter() iter.Seq[T]

Iter returns an iterator over the set.

Example
s := set.FromSlice("a", "b", "c")

t := []string{}
for k := range s.Iter() {
	t = append(t, k)
}

slices.Sort(t)
fmt.Printf(
	"%d %v",
	len(t),
	t,
)
Output:

3 [a b c]

func (Set[T]) List

func (s Set[T]) List() []T

List returns the set as the original array. Order is not preserved.

Example
s := set.FromSlice("a", "b")
name := fmt.Sprintf("%v", s.List())
re := regexp.MustCompile(`^(\[a b\]|\[b a\])$`)
fmt.Println(re.Match([]byte(name)))
Output:

true

func (Set[T]) Remove

func (s Set[T]) Remove(items ...T)

Remove removes items from a set.

Example
s := set.FromSlice("a", "b", "c")
s.Remove("b", "c")
fmt.Printf(
	"%d %t %t %t",
	len(s),
	s.Contains("a"),
	s.Contains("b"),
	s.Contains("c"),
)
Output:

1 true false false

func (Set[T]) String added in v0.2.0

func (s Set[T]) String() string

String returns a string representation of a set.

Example
s := set.FromSlice("a", "b")
name := s.String()
re := regexp.MustCompile(`^({a b}|{b a})$`)
fmt.Println(re.Match([]byte(name)))
Output:

true
Example (Nil)
var s set.Set[int]
fmt.Printf("%v", s)
Output:

{}

func (Set[T]) Sub added in v1.0.0

func (s Set[T]) Sub(other Set[T]) bool

Sub returns true if other is a subset of set.

Example
a := []int{1, 2}
b := []int{1, 2}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Sub(t),
)
Output:

true
Example (Larger)
a := []int{1, 2, 3}
b := []int{1, 2}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Sub(t),
)
Output:

true
Example (Not)
a := []int{1, 2, 3}
b := []int{1, 4}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Sub(t),
)
Output:

false
Example (Smaller)
a := []int{1, 2}
b := []int{1, 2, 3}
s := set.FromSlice(a...)
t := set.FromSlice(b...)
fmt.Printf(
	"%t",
	s.Sub(t),
)
Output:

false

func (Set[T]) SymmetricDifference

func (s Set[T]) SymmetricDifference(other Set[T]) Set[T]

SymmetricDifference returns set that consists of items that are in each set but not in both sets.

Example
a := []string{"a", "b", "c"}
m := []string{"c", "d", "e"}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.SymmetricDifference(t)
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains("a"),
	u.Contains("b"),
	u.Contains("c"),
	u.Contains("d"),
	u.Contains("e"),
)
Output:

4 true true false true true

func (Set[T]) Union

func (s Set[T]) Union(other Set[T]) Set[T]

Union returns set that consists of items that are in either of the 2 sets.

Example
a := []string{"a", "b", "c"}
m := []string{"c", "d", "e"}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.Union(t)
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains("a"),
	u.Contains("b"),
	u.Contains("c"),
	u.Contains("d"),
	u.Contains("e"),
)
Output:

5 true true true true true
Example (Int)
a := []int{1, 2, 3}
m := []int{3, 4, 5}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.Union(t)
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains(1),
	u.Contains(2),
	u.Contains(3),
	u.Contains(4),
	u.Contains(5),
)
Output:

5 true true true true true
Example (Struct)
type testStruct struct {
	name  string
	index int
}

oneTestStruct := testStruct{"one", 1}
twoTestStruct := testStruct{"two", 2}
threeTestStruct := testStruct{"three", 3}
fourTestStruct := testStruct{"four", 4}
fiveTestStruct := testStruct{"five", 5}

a := []testStruct{oneTestStruct, twoTestStruct, threeTestStruct}
m := []testStruct{threeTestStruct, fourTestStruct, fiveTestStruct}
s := set.FromSlice(a...)
t := set.FromSlice(m...)
u := s.Union(t)
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains(oneTestStruct),
	u.Contains(twoTestStruct),
	u.Contains(threeTestStruct),
	u.Contains(fourTestStruct),
	u.Contains(fiveTestStruct),
)
Output:

5 true true true true true

func (Set[T]) UnionIter

func (s Set[T]) UnionIter(items iter.Seq[T]) Set[T]

UnionIter returns set that consists of items that are in either the set or iterable.

Example
a := []string{"a", "b", "c"}
m := map[string]int{"c": 0, "d": 1, "e": 2}
s := set.FromSlice(a...)
u := s.UnionIter(maps.Keys(m))
fmt.Printf(
	"%d %t %t %t %t %t",
	len(u),
	u.Contains("a"),
	u.Contains("b"),
	u.Contains("c"),
	u.Contains("d"),
	u.Contains("e"),
)
Output:

5 true true true true true

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL