kset

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 1, 2025 License: MIT Imports: 6 Imported by: 0

README

KSET - Generic Set Implementation for Go

Go Reference Tests

kset provides a flexible and type-safe implementation of a mathematical set data structure in Go.
It requires Go 1.23+ as it uses iterators and generics.

It allows you to work with sets of keys or key-values.
It easily converts from slices
The key set and key-value set are compatible with one another
You can use sets of different data types as long as their keys are from the same data type.

Features

  • Type-Safe: Compile-time type checking thanks to Go generics.
  • Key-Based: Uniqueness is determined by a user-provided selector function.
  • Comprehensive API: Implements standard set operations like Union, Intersection, Difference, Symmetric Difference, Subset/Superset checks, etc.
  • Iterable: Provides an iter.Seq[V] method compatible with Go 1.22+.
  • Selectable Underlying Data Structure Allows you to choose either a hash-table or a tree-map structure for the sets.

Installation

go get github.com/sonalys/kset

Usage

Using Custom Types You can use kset with your own structs by providing an appropriate selector.

package main

import (
	"fmt"
	"slices"

	"github.com/sonalys/kset"
)

func ExampleHashMapKeyValue() {
	type User struct {
		ID   int
		Name string
	}

	userIDSelector := func(u User) int { return u.ID }

	userSet := kset.HashMapKeyValue(userIDSelector,
		User{ID: 1, Name: "Alice"},
		User{ID: 2, Name: "Bob"},
	)

	// Adding a user with the same key will upsert the user.
	// Returns true if the entry is introducing a new key.
	addedCount := userSet.Append(User{ID: 1, Name: "Alice Smith"})
	fmt.Printf("Added: %v\n", addedCount)

	elements := userSet.Slice()
	slices.SortFunc(elements, func(a, b User) int {
		return a.ID - b.ID
	})

	fmt.Printf("Set Elements: %+v\n", elements)
	// Output:
	// Added: 0
	// Set Elements: [{ID:1 Name:Alice Smith} {ID:2 Name:Bob}]
}

Here's a basic example using a set of integers:

func ExampleTreeMapKey() {
	setA := kset.TreeMapKey(1, 2, 3, 1)

	sortSlice := func(slice []int) []int {
		slices.Sort(slice)
		return slice
	}

	fmt.Printf("Set: %v\n", sortSlice(setA.Slice()))
	fmt.Printf("Length: %d\n", setA.Len())
	fmt.Printf("Contains 2? %t\n", setA.ContainsKeys(2))
	fmt.Printf("Contains 4? %t\n", setA.ContainsKeys(4))

	setB := kset.TreeMapKey(3, 4, 5)
	setB.Append(3, 4, 5)

	// Set operations
	union := setA.Union(setB)
	intersection := setA.Intersect(setB)
	// Elements in intSet but not in otherSet
	difference := setA.Difference(setB)
	symDifference := setA.SymmetricDifference(setB)

	fmt.Printf("Other Set: %v\n", sortSlice(setB.Slice()))
	fmt.Printf("Union: %v\n", sortSlice(union.Slice()))
	fmt.Printf("Intersection: %v\n", sortSlice(intersection.Slice()))
	fmt.Printf("Difference (setA - setB): %v\n", sortSlice(difference.Slice()))
	fmt.Printf("Symmetric Difference: %v\n", sortSlice(symDifference.Slice()))

	// Output:
	// Set: [1 2 3]
	// Length: 3
	// Contains 2? true
	// Contains 4? false
	// Other Set: [3 4 5]
	// Union: [1 2 3 4 5]
	// Intersection: [3]
	// Difference (setA - setB): [1 2]
	// Symmetric Difference: [1 2 4 5]
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Select added in v0.4.3

func Select[Key, Value any](selector func(Value) Key, values ...Value) []Key

Types

type KeySet added in v0.2.0

type KeySet[Key any] interface {
	Set[Key]

	// Append upserts multiple elements to the set.
	// It returns the number of elements that were actually added (i.e., were not already present).
	// Example:
	//  s := kset.HashMapKey(1)
	//  count := s.Append(1, 2, 3) // count is 2
	Append(values ...Key) int

	// Clone creates a shallow copy of the set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := s1.Clone() // s2 is {1, 2}, independent of s1
	//  s2.Add(3)
	//  // s1 is {1, 2}, s2 is {1, 2, 3}
	Clone() KeySet[Key]

	// Difference returns a new set containing elements that are in the current set but not in the other set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(3, 4, 5)
	//  diff := s1.Difference(s2) // diff is {1, 2}
	Difference(other Set[Key]) KeySet[Key]

	// DifferenceKeys returns a new set, not containing the given keys.
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(3, 4, 5)
	//  diff := s1.Difference(s2) // diff is {1, 2}
	DifferenceKeys(keys ...Key) KeySet[Key]

	// Intersect returns a new set containing elements that are common to both the current set and the other set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(3, 4, 5)
	//  intersection := s1.Intersect(s2) // intersection is {3}
	Intersect(other Set[Key]) KeySet[Key]

	// RemoveKeys removes the specified elements from the set.
	// Example:
	//  s := kset.HashMapKey(1, 2, 3, 4)
	//  s.RemoveKeys(2, 4) // s is {1, 3}
	RemoveKeys(v ...Key)

	// SymmetricDifference returns a new set containing elements that are in either the current set or the other set, but not both.
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(3, 4, 5)
	//  symDiff := s1.SymmetricDifference(s2) // symDiff is {1, 2, 4, 5}
	SymmetricDifference(other KeySet[Key]) KeySet[Key]

	// Union returns a new set containing all elements from both the current set and the other set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := kset.HashMapKey(2, 3)
	//  union := s1.Union(s2) // union is {1, 2, 3}
	Union(other KeySet[Key]) KeySet[Key]

	// Pop removes and returns an arbitrary element from the set.
	// It returns the removed element and true if the set was not empty, otherwise it returns the zero value of V and false.
	// Example:
	//  s := kset.HashMapKey(1, 2)
	//  v, ok := s.Pop() // v could be 1 or 2, ok is true
	//  v, ok = s.Pop() // v is the remaining element, ok is true
	//  v, ok = s.Pop() // v is 0, ok is false
	Pop() (Key, bool)

	// Slice returns a slice containing all elements of the set.
	// The order of elements in the slice is not guaranteed.
	// Example:
	//  s := kset.HashMapKey(3, 1, 2)
	//  slice := s.Slice() // slice could be []int{1, 2, 3}, []int{3, 1, 2}, etc.
	Slice() []Key
}

KeySet defines a key-only set. It performs mathematical set operations on a batch of values. The underlying data structure used for the set is dependable on the used constructor.

func HashMapKey added in v0.4.3

func HashMapKey[Key comparable](values ...Key) KeySet[Key]

HashMapKey is a thread-safe hash table key set implementation.

Operation		Average		WorstCase
Search			O(1)		O(logN^2)
Insert			O(1)		O(logN^2)
Delete			O(1)		O(n)

Space complexity

Space			O(n)		O(n)

func TreeMapKey added in v0.4.3

func TreeMapKey[Key constraints.Ordered](keys ...Key) KeySet[Key]

TreeMapKey is a thread-safe red-black tree key set implementation.

Operation		Average		WorstCase
Search			O(logN)		O(logN)
Insert			O(logN)		O(logN)
Delete			O(logN)		O(logN)

Space complexity

Space			O(n)		O(n)
Example
package main

import (
	"fmt"
	"slices"

	"github.com/sonalys/kset"
)

func main() {
	setA := kset.TreeMapKey(1, 2, 3, 1)

	sortSlice := func(slice []int) []int {
		slices.Sort(slice)
		return slice
	}

	fmt.Printf("Set: %v\n", sortSlice(setA.Slice()))
	fmt.Printf("Length: %d\n", setA.Len())
	fmt.Printf("Contains 2? %t\n", setA.ContainsKeys(2))
	fmt.Printf("Contains 4? %t\n", setA.ContainsKeys(4))

	setB := kset.TreeMapKey(3, 4, 5)
	setB.Append(3, 4, 5)

	// Set operations
	union := setA.Union(setB)
	intersection := setA.Intersect(setB)
	// Elements in intSet but not in otherSet
	difference := setA.Difference(setB)
	symDifference := setA.SymmetricDifference(setB)

	fmt.Printf("Other Set: %v\n", sortSlice(setB.Slice()))
	fmt.Printf("Union: %v\n", sortSlice(union.Slice()))
	fmt.Printf("Intersection: %v\n", sortSlice(intersection.Slice()))
	fmt.Printf("Difference (setA - setB): %v\n", sortSlice(difference.Slice()))
	fmt.Printf("Symmetric Difference: %v\n", sortSlice(symDifference.Slice()))

}
Output:

Set: [1 2 3]
Length: 3
Contains 2? true
Contains 4? false
Other Set: [3 4 5]
Union: [1 2 3 4 5]
Intersection: [3]
Difference (setA - setB): [1 2]
Symmetric Difference: [1 2 4 5]

func UnsafeHashMapKey added in v0.4.3

func UnsafeHashMapKey[Key comparable](values ...Key) KeySet[Key]

UnsafeHashMapKey is a thread-unsafe hash table key set implementation.

Operation		Average		WorstCase
Search			O(1)		O(logN^2)
Insert			O(1)		O(logN^2)
Delete			O(1)		O(n)

Space complexity

Space			O(n)		O(n)

func UnsafeTreeMapKey added in v0.4.3

func UnsafeTreeMapKey[Key constraints.Ordered](keys ...Key) KeySet[Key]

UnsafeTreeMapKey is a thread-unsafe red-black tree key set implementation.

Operation		Average		WorstCase
Search			O(logN)		O(logN)
Insert			O(logN)		O(logN)
Delete			O(logN)		O(logN)

Space complexity

Space			O(n)		O(n)

type KeyValueSet added in v0.3.0

type KeyValueSet[Key comparable, Value any] interface {
	Set[Key]

	// Append upserts multiple elements to the set.
	// It returns the number of elements that were actually added (i.e., were not already present).
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1)
	//  count := s.Append(1, 2, 3) // count is 2
	Append(values ...Value) int

	// Clone creates a shallow copy of the set.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2)
	//  s2 := s1.Clone() // s2 is {1, 2}, independent of s1
	//  s2.Add(3)
	//  // s1 is {1, 2}, s2 is {1, 2, 3}
	Clone() KeyValueSet[Key, Value]

	// Contains checks if all specified elements are present in the set.
	// It returns true if all elements v are in the set, false otherwise.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  hasAll := s.Contains(1, 2) // hasAll is true
	//  hasAll = s.Contains(1, 4) // hasAll is false
	Contains(values ...Value) bool

	// ContainsAny checks if any of the specified elements are present in the set.
	// It returns true if at least one element v is in the set, false otherwise.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2)
	//  hasAny := s.ContainsAny(2, 4) // hasAny is true
	//  hasAny = s.ContainsAny(4, 5) // hasAny is false
	ContainsAny(values ...Value) bool

	// Difference returns a new set containing elements that are in the current set but not in the other set.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  s2 := kset.HashMapKeyValue(func(v int) int { return v }, 3, 4, 5)
	//  diff := s1.Difference(s2) // diff is {1, 2}
	Difference(other Set[Key]) KeyValueSet[Key, Value]

	// DifferenceKeys returns a copy of the current set, excluding the given keys.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  diff := s1.DifferenceKeys(3) // diff is {1, 2}
	DifferenceKeys(keys ...Key) KeyValueSet[Key, Value]

	// Intersect returns a new set containing elements that are common to both the current set and the other set.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  s2 := kset.HashMapKeyValue(func(v int) int { return v }, 3, 4, 5)
	//  intersection := s1.Intersect(s2) // intersection is {3}
	Intersect(other Set[Key]) KeyValueSet[Key, Value]

	// KeyValues returns an iterator (iter.Seq) over the elements of the set.
	// The order of iteration is not guaranteed.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  for v := range s.KeyValues() {
	//      fmt.Println(v) // Prints 1, 2, 3 in some order
	//  }
	KeyValues() iter.Seq2[Key, Value]

	// Remove removes the specified elements from the set.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3, 4)
	//  s.Remove(2, 4) // s is {1, 3}
	Remove(v ...Value)

	// RemoveKeys removes the specified keys from the set.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3, 4)
	//  s.RemoveKeys(2, 4) // s is {1, 3}
	RemoveKeys(v ...Key)

	// SymmetricDifference returns a new set containing elements that are in either the current set or the other set, but not both.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2, 3)
	//  s2 := kset.HashMapKeyValue(func(v int) int { return v }, 3, 4, 5)
	//  symDiff := s1.SymmetricDifference(s2) // symDiff is {1, 2, 4, 5}
	SymmetricDifference(other KeyValueSet[Key, Value]) KeyValueSet[Key, Value]

	// Union returns a new set containing all elements from both the current set and the other set.
	// Example:
	//  s1 := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2)
	//  s2 := kset.HashMapKeyValue(func(v int) int { return v }, 2, 3)
	//  union := s1.Union(s2) // union is {1, 2, 3}
	Union(other KeyValueSet[Key, Value]) KeyValueSet[Key, Value]

	// Pop removes and returns an arbitrary element from the set.
	// It returns the removed element and true if the set was not empty, otherwise it returns the zero value of V and false.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 1, 2)
	//  v, ok := s.Pop() // v could be 1 or 2, ok is true
	//  v, ok = s.Pop() // v is the remaining element, ok is true
	//  v, ok = s.Pop() // v is 0, ok is false
	Pop() (Value, bool)

	// Slice returns a slice containing all elements of the set.
	// The order of elements in the slice is not guaranteed.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 3, 1, 2)
	//  slice := s.Slice() // slice could be []int{1, 2, 3}, []int{3, 1, 2}, etc.
	Slice() []Value

	// Map returns a map representation of the set.
	// Example:
	//  s := kset.HashMapKeyValue(func(v int) int { return v }, 3, 1, 2)
	//  m := s.Map() // m returns map[int]int{ 1:1, 2:2, 3:3 }
	Map() map[Key]Value
}

KeyValueSet is a key-value set. It performs mathematical set operations on a batch of values. It uses from a selector to extract keys from any given value. The underlying data structure used for the set is dependable on the used constructor.

func HashMapKeyValue added in v0.4.3

func HashMapKeyValue[Key comparable, Value any](selector func(Value) Key, values ...Value) KeyValueSet[Key, Value]

HashMapKeyValue is a thread-safe hash table key-value set implementation.

Operation		Average		WorstCase
Search			O(1)		O(logN^2)
Insert			O(1)		O(logN^2)
Delete			O(1)		O(n)

Space complexity

Space			O(n)		O(n)
Example
package main

import (
	"fmt"
	"slices"

	"github.com/sonalys/kset"
)

func main() {
	type User struct {
		ID   int
		Name string
	}

	userIDSelector := func(u User) int { return u.ID }

	userSet := kset.HashMapKeyValue(userIDSelector,
		User{ID: 1, Name: "Alice"},
		User{ID: 2, Name: "Bob"},
	)

	// Adding a user with the same key will upsert the user.
	// Returns true if the entry is introducing a new key.
	addedCount := userSet.Append(User{ID: 1, Name: "Alice Smith"})
	fmt.Printf("Added: %v\n", addedCount)

	elements := userSet.Slice()
	slices.SortFunc(elements, func(a, b User) int {
		return a.ID - b.ID
	})

	fmt.Printf("Set Elements: %+v\n", elements)
}
Output:

Added: 0
Set Elements: [{ID:1 Name:Alice Smith} {ID:2 Name:Bob}]

func TreeMapKeyValue added in v0.4.3

func TreeMapKeyValue[Key constraints.Ordered, Value any](selector func(Value) Key, values ...Value) KeyValueSet[Key, Value]

TreeMapKeyValue is a thread-safe red-black tree key-value set implementation.

Operation		Average		WorstCase
Search			O(logN)		O(logN)
Insert			O(logN)		O(logN)
Delete			O(logN)		O(logN)

Space complexity

Space			O(n)		O(n)

func UnsafeHashMapKeyValue added in v0.4.3

func UnsafeHashMapKeyValue[Key comparable, Value any](selector func(Value) Key, values ...Value) KeyValueSet[Key, Value]

UnsafeHashMapKeyValue is a thread-unsafe hash table key-value set implementation.

Operation		Average		WorstCase
Search			O(1)		O(logN^2)
Insert			O(1)		O(logN^2)
Delete			O(1)		O(n)

Space complexity

Space			O(n)		O(n)

func UnsafeTreeMapKeyValue added in v0.4.3

func UnsafeTreeMapKeyValue[Key constraints.Ordered, Value any](selector func(Value) Key, values ...Value) KeyValueSet[Key, Value]

UnsafeTreeMapKeyValue is a thread-unsafe red-black tree key-value set implementation.

Operation		Average		WorstCase
Search			O(logN)		O(logN)
Insert			O(logN)		O(logN)
Delete			O(logN)		O(logN)

Space complexity

Space			O(n)		O(n)

type Set

type Set[Key any] interface {
	// Len returns the number of elements in the set.
	// Example:
	//  s := kset.HashMapKey(1, 2)
	//  length := s.Len() // length is 2
	Len() int

	// Clear removes all elements from the set.
	// Example:
	//  s := kset.HashMapKey(1, 2)
	//  s.Clear() // s is {}
	//  length := s.Len() // length is 0
	Clear()

	// Contains checks if all specified elements are present in the set.
	// It returns true if all elements v are in the set, false otherwise.
	// Example:
	//  s := kset.HashMapKey(1, 2, 3)
	//  hasAll := s.ContainsKeys(1, 2) // hasAll is true
	//  hasAll = s.ContainsKeys(1, 4) // hasAll is false
	ContainsKeys(keys ...Key) bool

	// ContainsAny checks if any of the specified elements are present in the set.
	// It returns true if at least one element v is in the set, false otherwise.
	// Example:
	//  s := kset.HashMapKey(1, 2)
	//  hasAny := s.ContainsAnyKey(2, 4) // hasAny is true
	//  hasAny = s.ContainsAnyKey(4, 5) // hasAny is false
	ContainsAnyKey(keys ...Key) bool

	// IsEmpty checks if the set contains no elements.
	// Example:
	//  s := kset.HashMapKey[int]()
	//  s.IsEmpty() // empty is true
	//  s.Add(1)
	//  empty = s.IsEmpty() // empty is false
	IsEmpty() bool

	// IsProperSubset checks if the set is a proper subset of another set.
	// A proper subset is a subset that is not equal to the other set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := kset.HashMapKey(1, 2, 3)
	//  s3 := kset.HashMapKey(1, 2)
	//  isProper := s1.IsProperSubset(s2) // isProper is true
	//  isProper = s1.IsProperSubset(s3) // isProper is false
	IsProperSubset(other Set[Key]) bool

	// IsProperSuperset checks if the set is a proper superset of another set.
	// A proper superset is a superset that is not equal to the other set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(1, 2)
	//  s3 := kset.HashMapKey(1, 2, 3)
	//  isProper := s1.IsProperSuperset(s2) // isProper is true
	//  isProper = s1.IsProperSuperset(s3) // isProper is false
	IsProperSuperset(other Set[Key]) bool

	// IsSubset checks if the set is a subset of another set (i.e., all elements of the current set are also in the other set).
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := kset.HashMapKey(1, 2, 3)
	//  s3 := kset.HashMapKey(1, 3)
	//  isSub := s1.IsSubset(s2) // isSub is true
	//  isSub = s1.IsSubset(s3) // isSub is false
	IsSubset(other Set[Key]) bool

	// IsSuperset checks if the set is a superset of another set (i.e., all elements of the other set are also in the current set).
	// Example:
	//  s1 := kset.HashMapKey(1, 2, 3)
	//  s2 := kset.HashMapKey(1, 2)
	//  s3 := kset.HashMapKey(1, 4)
	//  isSuper := s1.IsSuperset(s2) // isSuper is true
	//  isSuper = s1.IsSuperset(s3) // isSuper is false
	IsSuperset(other Set[Key]) bool

	// Intersects checks if the set has at least one element in common with another set.
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := kset.HashMapKey(2, 3)
	//  s3 := kset.HashMapKey(4, 5)
	//  intersects := s1.Intersects(s2) // intersects is true
	//  intersects = s1.Intersects(s3) // intersects is false
	Intersects(other Set[Key]) bool

	// Equal checks if the set is equal to another set (i.e., contains the same elements).
	// Example:
	//  s1 := kset.HashMapKey(1, 2)
	//  s2 := kset.HashMapKey(2, 1)
	//  s3 := kset.HashMapKey(1, 3)
	//  isEqual := s1.Equal(s2) // isEqual is true
	//  isEqual = s1.Equal(s3) // isEqual is false
	Equal(other Set[Key]) bool

	// Keys iterates through all keys stored in the set.
	// Example:
	//	set := kset.HashMapKey(1, 2, 3)
	//	set.Keys() // returns iter[1, 2, 3]
	Keys() iter.Seq[Key]
}

Set defines the interface of the behavior expected from only comparing keys, and not values. This interface is useful for comparing sets that shares the same key type, but not the same value.

type Storage added in v0.4.3

type Storage[Key, Value any] interface {
	Len() int
	Clear()
	Delete(...Key)
	Contains(Key) bool
	Get(Key) (Value, bool)
	Upsert(Key, Value)
	Iter() iter.Seq2[Key, Value]
	Clone() Storage[Key, Value]
}

Jump to

Keyboard shortcuts

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