collection

package module
v1.20.1 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2026 License: BSD-2-Clause Imports: 11 Imported by: 6

README

Collection

A comprehensive Go library providing generic, type-safe collection utilities and data structures. Leverages Go generics for efficient slice operations, set management, channel processing, and pointer utilities.

Features

  • 🔧 Slice Operations: Filter, Map, Find, Contains, Unique, Reverse, Copy, Exclude, Join
  • 📦 Set Data Structure: Thread-safe generic set with mutex protection
  • ⚡ Channel Processing: Concurrent channel operations with context support
  • 🎯 Pointer Utilities: Generic Ptr/Unptr functions for easy pointer creation
  • ⚖️ Comparison Utilities: Generic equality and string comparison helpers

Installation

go get github.com/bborbe/collection

Quick Start

import "github.com/bborbe/collection"

// Filter slice elements
numbers := []int{1, 2, 3, 4, 5}
evens := collection.Filter(numbers, func(n int) bool { return n%2 == 0 })
// Result: [2, 4]

// Create and use a Set
set := collection.NewSet[string]()
set.Add("hello")
set.Add("world")
fmt.Println(set.Contains("hello")) // true

// Create pointers easily
name := "John"
namePtr := collection.Ptr(name) // *string pointing to "John"

API Reference

Slice Operations
Filter
func Filter[T any](list []T, match func(value T) bool) []T

Returns a new slice containing only elements that match the predicate.

users := []User{{Name: "Alice", Age: 25}, {Name: "Bob", Age: 17}}
adults := collection.Filter(users, func(u User) bool { return u.Age >= 18 })
Find
func Find[T any](list []T, match func(value T) bool) (*T, error)

Returns the first element matching the predicate, or NotFoundError.

user, err := collection.Find(users, func(u User) bool { return u.Name == "Alice" })
if err != nil {
    // Handle not found
}
Map
func Map[T any](list []T, fn func(value T) error) error

Applies a function to each element in the slice, returning the first error encountered.

err := collection.Map(users, func(u User) error {
    return validateUser(u)
})
Unique
func Unique[T comparable](list []T) []T

Returns a new slice with duplicate elements removed, preserving order.

numbers := []int{1, 2, 2, 3, 3, 3}
unique := collection.Unique(numbers) // [1, 2, 3]
Contains
func Contains[T comparable](list []T, search T) bool
func ContainsAll[T comparable](list []T, search []T) bool

Check if slice contains specific element(s).

fruits := []string{"apple", "banana", "cherry"}
hasApple := collection.Contains(fruits, "apple") // true
hasAll := collection.ContainsAll(fruits, []string{"apple", "banana"}) // true
Reverse
func Reverse[T any](list []T) []T

Returns a new slice with elements in reverse order.

numbers := []int{1, 2, 3}
reversed := collection.Reverse(numbers) // [3, 2, 1]
Copy
func Copy[T any](list []T) []T

Creates a shallow copy of the slice.

Exclude
func Exclude[T comparable](list []T, exclude []T) []T

Returns a new slice excluding specified elements.

numbers := []int{1, 2, 3, 4, 5}
filtered := collection.Exclude(numbers, []int{2, 4}) // [1, 3, 5]
Join
func Join[T ~string](list []T, separator string) string

Joins string-like elements with a separator.

words := []string{"hello", "world", "!"}
sentence := collection.Join(words, " ") // "hello world !"
Set Data Structure
Creating Sets
func NewSet[T comparable]() Set[T]

Creates a new thread-safe generic set.

stringSet := collection.NewSet[string]()
intSet := collection.NewSet[int]()
Set Operations
type Set[T comparable] interface {
    Add(element T)
    Remove(element T) 
    Contains(element T) bool
    Slice() []T
    Length() int
}

Example usage:

set := collection.NewSet[string]()
set.Add("apple")
set.Add("banana")
set.Add("apple") // Duplicate, ignored

fmt.Println(set.Length()) // 2
fmt.Println(set.Contains("apple")) // true
fmt.Println(set.Slice()) // ["apple", "banana"] (order not guaranteed)

set.Remove("apple")
fmt.Println(set.Length()) // 1
Pointer Utilities
Ptr
func Ptr[T any](value T) *T

Creates a pointer to the given value.

name := "John"
namePtr := collection.Ptr(name) // *string

age := 25
agePtr := collection.Ptr(age) // *int
Unptr
func Unptr[T any](ptr *T) T
func UnptrWithDefault[T any](ptr *T, defaultValue T) T

Dereferences pointers safely.

var namePtr *string = collection.Ptr("John")
name := collection.Unptr(namePtr) // "John"

var nilPtr *string
name := collection.UnptrWithDefault(nilPtr, "Anonymous") // "Anonymous"
Channel Processing
ChannelFnMap
func ChannelFnMap[T interface{}](
    ctx context.Context,
    getFn func(ctx context.Context, ch chan<- T) error,
    mapFn func(ctx context.Context, t T) error,
) error

Processes data from a producer function through a mapper function concurrently.

err := collection.ChannelFnMap(
    ctx,
    func(ctx context.Context, ch chan<- int) error {
        for i := 0; i < 100; i++ {
            select {
            case ch <- i:
            case <-ctx.Done():
                return ctx.Err()
            }
        }
        return nil
    },
    func(ctx context.Context, num int) error {
        fmt.Printf("Processing: %d\n", num)
        return nil
    },
)
ChannelFnList & ChannelFnCount
func ChannelFnList[T any](ctx context.Context, getFn func(ctx context.Context, ch chan<- T) error) ([]T, error)
func ChannelFnCount[T any](ctx context.Context, getFn func(ctx context.Context, ch chan<- T) error) (int, error)

Collect channel data into a slice or count items.

Comparison Utilities
Equal & Compare
func Equal[T comparable](a, b T) bool
func Compare[T ~string](a, b T) int

Generic comparison functions for any comparable types.

Advanced Examples

Processing Large Datasets Concurrently
ctx := context.Background()

// Process items concurrently with automatic batching
err := collection.ChannelFnMap(
    ctx,
    func(ctx context.Context, ch chan<- WorkItem) error {
        return loadWorkItems(ctx, ch) // Your data loading logic
    },
    func(ctx context.Context, item WorkItem) error {
        return processWorkItem(ctx, item) // Your processing logic
    },
)
Building Complex Data Pipelines
// Load raw data
rawData := loadRawData()

// Filter valid entries
validData := collection.Filter(rawData, func(item DataItem) bool {
    return item.IsValid()
})

// Remove duplicates
uniqueData := collection.Unique(validData)

// Find specific items
importantItem, err := collection.Find(uniqueData, func(item DataItem) bool {
    return item.Priority == "high"
})

// Create a set for fast lookups
processedIDs := collection.NewSet[string]()
for _, item := range uniqueData {
    processedIDs.Add(item.ID)
}

Requirements

  • Go 1.24.5 or later
  • Support for Go generics

Dependencies

  • github.com/bborbe/errors - Enhanced error handling
  • github.com/bborbe/run - Concurrent execution utilities

Testing

go test ./...

License

BSD-style license. See LICENSE file for details.

Documentation

Overview

Package collection provides generic, type-safe collection utilities and data structures for Go.

This library leverages Go generics to offer a comprehensive set of operations for working with slices, sets, channels, and other collection types. It includes functional-style operations, concurrent-safe data structures, and utility functions for common collection manipulations.

Key Features:

- Slice Operations: Filter, Map, Find, Contains, Unique, Reverse, Copy, Exclude, Join - Set Data Structures: Thread-safe generic sets with Equal and HashCode support - Channel Processing: Concurrent operations with context support - Pointer Utilities: Generic Ptr/Unptr functions for value-pointer conversion - Comparison Utilities: Generic equality and string comparison helpers

The package follows functional programming patterns with higher-order functions while maintaining thread safety for concurrent operations through proper mutex synchronization.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = stderrors.New("not found")

ErrNotFound is returned when an element is not found in a collection.

View Source
var NotFoundError = ErrNotFound //nolint:errname // deprecated alias for backwards compatibility

NotFoundError is deprecated: use ErrNotFound instead. NotFoundError is kept for backward compatibility.

Functions

func ChannelFnCount added in v1.1.0

func ChannelFnCount[T interface{}](
	ctx context.Context,
	fn func(ctx context.Context, ch chan<- T) error,
) (int, error)

ChannelFnCount executes a function that sends values to a channel and returns the total count of values sent. Returns -1 if an error occurs.

func ChannelFnList

func ChannelFnList[T interface{}](
	ctx context.Context,
	fn func(ctx context.Context, ch chan<- T) error,
) ([]T, error)

ChannelFnList executes a function that sends values to a channel and collects all the values into a slice. Returns an empty slice if an error occurs.

func ChannelFnMap

func ChannelFnMap[T interface{}](
	ctx context.Context,
	getFn func(ctx context.Context, ch chan<- T) error,
	mapFn func(ctx context.Context, t T) error,
) error

ChannelFnMap executes a function that sends values to a channel and applies a mapping function to each value received from the channel. It runs the getter and mapper functions concurrently and returns the first error encountered.

func Compare added in v1.6.0

func Compare[T ~string](a, b T) int

Compare compares two string-like values and returns an integer comparing a and b. The result will be 0 if a == b, -1 if a < b, and +1 if a > b.

func Contains

func Contains[T comparable](list []T, value T) bool

Contains reports whether the given value is present in the slice.

func ContainsAll added in v1.2.0

func ContainsAll[T comparable](a []T, b []T) bool

ContainsAll returns true if all elements from slice b are present in slice a. It checks whether a is a superset of b (a ⊇ b). Empty slice b always returns true. Duplicate elements in b are treated as single elements.

Performance: O(len(a) + len(b)) time, O(len(a)) space. For very large slices (> 10M elements), consider validating input sizes.

func ContainsAny added in v1.13.0

func ContainsAny[T comparable](a []T, b []T) bool

ContainsAny returns true if at least one element from slice b is present in slice a. It checks whether a and b have any intersection (a ∩ b ≠ ∅). Empty slice b always returns false (no elements to check).

Performance: O(len(a) + len(b)) time, O(len(a)) space. For very large slices (> 10M elements), consider validating input sizes.

func Copy added in v1.3.1

func Copy[T any](values []T) []T

Copy returns a new slice that is a shallow copy of the input slice.

func Each added in v1.19.0

func Each[T any](
	ctx context.Context,
	list []T,
	fn func(ctx context.Context, value T) error,
) error

Each applies the given function to each element in the slice. If any function call returns an error, Each stops and returns that error.

func Equal added in v1.2.0

func Equal[T comparable](a []T, b []T) bool

Equal reports whether two slices are equal by comparing their lengths and elements in order.

func Exclude

func Exclude[T comparable](list []T, excludes ...T) []T

Exclude returns a new slice with all elements from the input slice except those specified in the excludes parameter.

func Filter

func Filter[T any](list []T, match func(value T) bool) []T

Filter returns a new slice containing only the elements from the input slice that satisfy the given predicate function.

func Find

func Find[T any](list []T, match func(value T) bool) (*T, error)

Find returns a pointer to the first element in the slice that satisfies the predicate function. If no element is found, it returns nil and ErrNotFound.

func Intersect added in v1.17.0

func Intersect[T comparable](a []T, b []T) []T

Intersect returns a new slice containing only the elements that appear in both input slices. It returns the intersection (a ∩ b) of the two slices. The order of elements in the result matches their order of first occurrence in slice a. Duplicate elements in the input slices are automatically handled.

Performance: O(len(a) + len(b)) time, O(len(b)) space.

func Join added in v1.7.0

func Join[T any](a []T, b []T) []T

Join allow to join two arrays into one new array

func Map added in v1.5.0

func Map[A any, B any](
	ctx context.Context,
	list []A,
	fn func(ctx context.Context, value A) (B, error),
) ([]B, error)

Map applies the given function to each element and returns a new slice with the transformed results. It transforms []A to []B by applying fn to each element of type A, producing elements of type B. If any function call returns an error, Map stops and returns that error along with the partially transformed slice.

func Ptr

func Ptr[T any](value T) *T

Ptr returns a pointer to the given value.

func Reverse

func Reverse[T any](values []T) []T

Reverse returns a new slice with the elements in reverse order.

func StreamList added in v1.8.0

func StreamList[T any](ctx context.Context, list []T, ch chan<- T) error

StreamList streams the given list into the given channel. It returns early if the context is canceled.

func UnPtr

func UnPtr[T any](ptr *T) T

UnPtr dereferences a pointer and returns the value. If the pointer is nil, it returns the zero value of type T.

func Unique

func Unique[T comparable](list []T) []T

Unique returns a new slice containing only the unique elements from the input slice. The order of the first occurrence of each element is preserved.

Types

type HasEqual added in v1.3.1

type HasEqual[V any] interface {
	Equal(value V) bool
}

HasEqual represents types that can compare themselves for equality with another value.

type HasHashCode added in v1.3.1

type HasHashCode interface {
	HashCode() string
}

HasHashCode represents types that can provide a string hash code for themselves.

Security: HashCode() MUST return unique values for distinct elements. Hash collisions will cause silent element overwrites in SetHashCode. Recommended: Use fmt.Sprintf("%#v", obj) for comprehensive hashing.

type Set added in v1.3.0

type Set[T comparable] interface {
	// Add inserts elements into the set. Duplicate elements are automatically ignored.
	// Multiple elements can be added in a single call with only one mutex lock.
	Add(elements ...T)
	// Remove deletes elements from the set.
	// Multiple elements can be removed in a single call with only one mutex lock.
	Remove(elements ...T)
	// Contains reports whether an element is present in the set.
	Contains(element T) bool
	// ContainsAll reports whether all given elements are present in the set.
	ContainsAll(elements ...T) bool
	// ContainsAny reports whether at least one of the given elements is present in the set.
	ContainsAny(elements ...T) bool
	// Slice returns all elements as a slice in arbitrary order.
	Slice() []T
	// Length returns the number of elements in the set.
	Length() int
	// Strings returns all elements as their string representations in sorted order.
	// This provides deterministic output suitable for debugging and logging.
	Strings() []string
	// String returns a human-readable string representation of the set.
	String() string
	// Each calls fn for each element in the set. Iteration stops on first error.
	// The order of iteration is arbitrary and not guaranteed to be consistent.
	Each(ctx context.Context, fn func(ctx context.Context, value T) error) error
	// Clone returns a new Set containing all elements from the current set.
	// The returned set is a shallow copy - modifications to it won't affect the original.
	Clone() Set[T]
	// Without returns a new Set containing all elements from the current set
	// except those specified in the elements parameter.
	// The original set is not modified.
	Without(elements ...T) Set[T]
	// UnmarshalText parses comma-separated text into set elements.
	// It implements encoding.TextUnmarshaler for automatic parsing with argument packages.
	UnmarshalText(text []byte) error
	// MarshalText converts set elements to comma-separated text.
	// It implements encoding.TextMarshaler for automatic serialization.
	MarshalText() ([]byte, error)
	// UnmarshalJSON deserializes a JSON array into set elements.
	// It implements json.Unmarshaler for automatic JSON parsing.
	UnmarshalJSON(data []byte) error
	// MarshalJSON serializes set elements to a JSON array.
	// It implements json.Marshaler for automatic JSON serialization.
	MarshalJSON() ([]byte, error)
}

Set represents a thread-safe collection of unique elements. This implementation uses a map for O(1) average-case lookups, additions, and deletions.

func NewSet added in v1.3.0

func NewSet[T comparable](elements ...T) Set[T]

NewSet creates a new thread-safe set for comparable types. It accepts optional initial elements to populate the set. Duplicate elements are automatically handled.

Performance: This implementation uses a map-based approach with O(1) average-case operations for Add, Remove, and Contains. Initialization is O(n) for n elements.

Example:

set := collection.NewSet(1, 2, 3)
empty := collection.NewSet[int]()

func ParseSetFromString added in v1.14.0

func ParseSetFromString[T ~string](value string) Set[T]

ParseSetFromString parses a comma-separated string into a Set with string-based type. T must be string or a type based on string (using ~string constraint).

func ParseSetFromStrings added in v1.14.0

func ParseSetFromStrings[T ~string](values []string) Set[T]

ParseSetFromStrings converts a slice of strings into a Set with string-based type. T must be string or a type based on string (using ~string constraint).

type SetEqual added in v1.3.1

type SetEqual[T HasEqual[T]] interface {
	// Add inserts elements into the set, using the Equal method for uniqueness checking.
	// Duplicate elements are automatically ignored.
	// Multiple elements can be added in a single call with only one mutex lock.
	Add(elements ...T)
	// Remove deletes elements from the set using its Equal method for matching.
	// Multiple elements can be removed in a single call with only one mutex lock.
	Remove(elements ...T)
	// Contains reports whether an element matching the given value is present in the set.
	Contains(element T) bool
	// ContainsAll reports whether all given elements are present in the set using the Equal method.
	ContainsAll(elements ...T) bool
	// ContainsAny reports whether at least one of the given elements is present in the set using the Equal method.
	ContainsAny(elements ...T) bool
	// Slice returns all elements as a slice in arbitrary order.
	Slice() []T
	// Length returns the number of elements in the set.
	Length() int
	// Strings returns all elements as their string representations in sorted order.
	// This provides deterministic output suitable for debugging and logging.
	Strings() []string
	// String returns a human-readable string representation of the set.
	String() string
	// Each calls fn for each element in the set. Iteration stops on first error.
	// Elements are iterated in insertion order (FIFO).
	Each(ctx context.Context, fn func(ctx context.Context, value T) error) error
	// Clone returns a new SetEqual containing all elements from the current set.
	// The returned set is a shallow copy - modifications to it won't affect the original.
	Clone() SetEqual[T]
	// Without returns a new SetEqual containing all elements from the current set
	// except those specified in the elements parameter.
	// The original set is not modified.
	Without(elements ...T) SetEqual[T]
	// UnmarshalJSON deserializes a JSON array into set elements.
	// It implements json.Unmarshaler for automatic JSON parsing.
	UnmarshalJSON(data []byte) error
	// MarshalJSON serializes set elements to a JSON array.
	// It implements json.Marshaler for automatic JSON serialization.
	MarshalJSON() ([]byte, error)
}

SetEqual represents a thread-safe set for types that implement HasEqual. Elements are uniquely identified by their Equal method.

Performance: This implementation uses a slice-based approach. Operations have O(n) complexity where n is the number of elements. For large sets or performance-critical code, consider using SetHashCode which provides O(1) average-case operations.

func NewSetEqual added in v1.3.1

func NewSetEqual[T HasEqual[T]](elements ...T) SetEqual[T]

NewSetEqual creates a new thread-safe set for types that implement HasEqual. It accepts optional initial elements to populate the set. Duplicate elements are automatically handled using the Equal method.

Performance: This implementation uses a slice-based approach with O(n) operations. Initialization with k elements has O(k²) complexity due to uniqueness checks. For better performance with large sets, use NewSetHashCode instead.

Example:

type User struct { ID int; Name string }
func (u User) Equal(other User) bool { return u.ID == other.ID }
set := collection.NewSetEqual(User{1, "Alice"}, User{2, "Bob"})

type SetHashCode added in v1.3.1

type SetHashCode[T HasHashCode] interface {
	// Add inserts elements into the set, using their hash codes for uniqueness.
	// Duplicate elements (same hash code) are automatically ignored.
	// Multiple elements can be added in a single call with only one mutex lock.
	Add(elements ...T)
	// Remove deletes elements from the set by their hash codes.
	// Multiple elements can be removed in a single call with only one mutex lock.
	Remove(elements ...T)
	// Contains reports whether an element with the given hash code is present in the set.
	Contains(element T) bool
	// ContainsAll reports whether all given elements are present in the set by their hash codes.
	ContainsAll(elements ...T) bool
	// ContainsAny reports whether at least one of the given elements is present in the set by their hash codes.
	ContainsAny(elements ...T) bool
	// Slice returns all elements as a slice in arbitrary order.
	Slice() []T
	// Length returns the number of elements in the set.
	Length() int
	// Strings returns all elements as their string representations in sorted order.
	// This provides deterministic output suitable for debugging and logging.
	Strings() []string
	// String returns a human-readable string representation of the set.
	String() string
	// Each calls fn for each element in the set. Iteration stops on first error.
	// The order of iteration is arbitrary and not guaranteed to be consistent.
	Each(ctx context.Context, fn func(ctx context.Context, value T) error) error
	// Clone returns a new SetHashCode containing all elements from the current set.
	// The returned set is a shallow copy - modifications to it won't affect the original.
	Clone() SetHashCode[T]
	// Without returns a new SetHashCode containing all elements from the current set
	// except those specified in the elements parameter.
	// The original set is not modified.
	Without(elements ...T) SetHashCode[T]
	// UnmarshalJSON deserializes a JSON array into set elements.
	// It implements json.Unmarshaler for automatic JSON parsing.
	UnmarshalJSON(data []byte) error
	// MarshalJSON serializes set elements to a JSON array.
	// It implements json.Marshaler for automatic JSON serialization.
	MarshalJSON() ([]byte, error)
}

SetHashCode represents a thread-safe set for types that implement HasHashCode. Elements are uniquely identified by their hash code.

Performance: This implementation uses a map-based approach with O(1) average-case operations for Add, Remove, and Contains. This provides better performance than SetEqual for large sets or performance-critical code.

func NewSetHashCode added in v1.3.1

func NewSetHashCode[T HasHashCode](elements ...T) SetHashCode[T]

NewSetHashCode creates a new thread-safe set for types that implement HasHashCode. It accepts optional initial elements to populate the set. Duplicate elements (same hash code) are automatically handled.

Performance: This implementation uses a map-based approach with O(1) average-case operations. Initialization is O(n) for n elements, making it suitable for large sets.

Example:

type User struct { ID int; Name string }
func (u User) HashCode() string { return fmt.Sprintf("user-%d", u.ID) }
set := collection.NewSetHashCode(User{1, "Alice"}, User{2, "Bob"})

Jump to

Keyboard shortcuts

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