common

package
v0.1.20 Latest Latest
Warning

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

Go to latest
Published: May 27, 2026 License: GPL-3.0 Imports: 7 Imported by: 0

README

common

common is a Go utility library providing generic collection operations and reflection-based utilities for working with slices, arrays, and maps. It offers functional programming patterns like map, filter, reduce, and more, all without requiring type-specific implementations.

Overview

The common package leverages Go's reflection capabilities to provide type-agnostic utility functions for collections. It solves the problem of repetitive code for common collection operations by offering:

  • Functional Operations: Transform, filter, reduce, and iterate over collections
  • Collection Utilities: Find, count, partition, unique, sort, and reverse
  • Set Operations: Difference, intersection, union-like operations
  • Comparison Utilities: Deep equality checking for any type
  • Type Checking: Scalar type validation and empty value detection

Problem Solved: Before Go 1.18 generics, developers needed to write type-specific implementations for common collection operations. While generics now exist, this package provides reflection-based alternatives that work with interface{}, making it useful for dynamic scenarios where types aren't known at compile time.

Use Cases

When to Use
  • Dynamic data processing - when types are unknown at compile time
  • Generic utilities - building libraries that work with any type
  • Reflection-based operations - when working with interface{} types
  • Functional programming patterns - map/filter/reduce on arbitrary collections
  • Testing utilities - comparing complex nested structures
  • Data transformations - converting between collection formats
  • Legacy code - working with pre-generics codebases
When Not to Use
  • Type-safe operations - use Go 1.18+ generics instead (e.g., coll package)
  • Performance-critical paths - reflection has overhead
  • Simple operations - use standard library or type-specific code
  • Compile-time safety needed - reflection bypasses type checking
  • Large datasets - consider streaming or database operations

Installation

go get github.com/sivaosorg/replify

Import the package in your Go code:

import "github.com/sivaosorg/replify/pkg/common"

Requirements: Go 1.13 or higher

Usage

Quick Start
package main

import (
    "fmt"
    "github.com/sivaosorg/replify/pkg/common"
)

func main() {
    // Filter even numbers
    numbers := []int{1, 2, 3, 4, 5, 6}
    evens := common.Filter(numbers, func(v interface{}) bool {
        return v.(int)%2 == 0
    })
    fmt.Println(evens) // [2 4 6]
    
    // Transform (map) - square each number
    squared := common.Transform(numbers, func(v interface{}) interface{} {
        return v.(int) * v.(int)
    })
    fmt.Println(squared) // [1 4 9 16 25 36]
    
    // Find first element > 3
    found := common.Find(numbers, func(v interface{}) bool {
        return v.(int) > 3
    })
    fmt.Println(found) // 4
    
    // Check if contains
    hasThree := common.Contains(numbers, 3)
    fmt.Println(hasThree) // true
}

Examples

1. Functional Operations
Transform (Map)
// Square numbers
numbers := []int{1, 2, 3, 4, 5}
squared := common.Transform(numbers, func(v interface{}) interface{} {
    return v.(int) * v.(int)
})
fmt.Println(squared) // [1 4 9 16 25]

// Convert to strings
strings := common.Transform(numbers, func(v interface{}) interface{} {
    return fmt.Sprintf("num-%d", v.(int))
})
fmt.Println(strings) // ["num-1" "num-2" "num-3" "num-4" "num-5"]
Filter
// Filter even numbers
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
evens := common.Filter(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(evens) // [2 4 6 8]

// Filter strings by length
words := []string{"hi", "hello", "world", "a"}
longWords := common.Filter(words, func(v interface{}) bool {
    return len(v.(string)) > 2
})
fmt.Println(longWords) // ["hello" "world"]
Reduce
// Sum of numbers
numbers := []int{1, 2, 3, 4, 5}
sum := common.Reduce(numbers, func(acc interface{}, v interface{}) interface{} {
    return acc.(int) + v.(int)
}, 0)
fmt.Println(sum) // 15

// Concatenate strings
words := []string{"Hello", "World", "!"}
sentence := common.Reduce(words, func(acc interface{}, v interface{}) interface{} {
    return acc.(string) + " " + v.(string)
}, "")
fmt.Println(sentence) // " Hello World !"
ReduceRight
// Reduce from right to left
numbers := []int{1, 2, 3, 4}
result := common.ReduceRight(numbers, func(acc interface{}, v interface{}) interface{} {
    return fmt.Sprintf("%v, %v", acc, v)
}, "start")
fmt.Println(result) // "start, 4, 3, 2, 1"
2. Collection Queries
Find
// Find first even number
numbers := []int{1, 3, 5, 6, 7, 8}
firstEven := common.Find(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(firstEven) // 6

// Find returns nil if not found
notFound := common.Find([]int{1, 3, 5}, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(notFound) // <nil>
All
// Check if all are positive
numbers := []int{1, 2, 3, 4, 5}
allPositive := common.All(numbers, func(v interface{}) bool {
    return v.(int) > 0
})
fmt.Println(allPositive) // true

// Check if all are even
allEven := common.All(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(allEven) // false
Any
// Check if any are negative
numbers := []int{1, 2, 3, 4, 5}
anyNegative := common.Any(numbers, func(v interface{}) bool {
    return v.(int) < 0
})
fmt.Println(anyNegative) // false

// Check if any are even
anyEven := common.Any(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(anyEven) // true
Count
// Count even numbers
numbers := []int{1, 2, 3, 4, 5, 6}
evenCount := common.Count(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(evenCount) // 3
3. Collection Manipulation
Remove
// Remove even numbers
numbers := []int{1, 2, 3, 4, 5, 6}
odds := common.Remove(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(odds) // [1 3 5]
Unique
// Remove duplicates
numbers := []int{1, 2, 2, 3, 4, 4, 5, 5, 5}
unique := common.Unique(numbers)
fmt.Println(unique) // [1 2 3 4 5]
Reverse
// Reverse in-place
numbers := []int{1, 2, 3, 4, 5}
common.Reverse(numbers)
fmt.Println(numbers) // [5 4 3 2 1]
Sort
// Custom sort (ascending)
numbers := []int{5, 2, 8, 1, 9}
common.Sort(numbers, func(i, j int) bool {
    return numbers[i] < numbers[j]
})
fmt.Println(numbers) // [1 2 5 8 9]

// Sort descending
common.Sort(numbers, func(i, j int) bool {
    return numbers[i] > numbers[j]
})
fmt.Println(numbers) // [9 8 5 2 1]
4. Set Operations
Contains
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(common.Contains(numbers, 3))  // true
fmt.Println(common.Contains(numbers, 10)) // false
Difference
// Elements in first collection but not in second
set1 := []int{1, 2, 3, 4, 5}
set2 := []int{3, 4, 5, 6, 7}
diff := common.Difference(set1, set2)
fmt.Println(diff) // [1 2]
Intersection
// Common elements
set1 := []int{1, 2, 3, 4, 5}
set2 := []int{3, 4, 5, 6, 7}
intersection := common.Intersection(set1, set2)
fmt.Println(intersection) // [3 4 5]
5. Partitioning and Slicing
Partition
// Split into evens and odds
numbers := []int{1, 2, 3, 4, 5, 6}
evens, odds := common.Partition(numbers, func(v interface{}) bool {
    return v.(int)%2 == 0
})
fmt.Println(evens) // [2 4 6]
fmt.Println(odds)  // [1 3 5]
Slice
// Extract subrange
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
sub := common.Slice(numbers, 2, 7)
fmt.Println(sub) // [2 3 4 5 6]
SliceWithIndices
// Extract specific indices
numbers := []int{10, 20, 30, 40, 50}
indices := []int{0, 2, 4}
selected := common.SliceWithIndices(numbers, indices)
fmt.Println(selected) // [10 30 50]
6. Advanced Operations
Zip
// Combine multiple collections
names := []string{"Alice", "Bob", "Charlie"}
ages := []int{30, 25, 35}
zipped := common.Zip(names, ages)
fmt.Println(zipped)
// [[Alice 30] [Bob 25] [Charlie 35]]
RotateLeft
numbers := []int{1, 2, 3, 4, 5}
rotated := common.RotateLeft(numbers, 2)
fmt.Println(rotated) // [3 4 5 1 2]
RotateRight
numbers := []int{1, 2, 3, 4, 5}
rotated := common.RotateRight(numbers, 2)
fmt.Println(rotated) // [4 5 1 2 3]
Iterate
// Custom iteration
numbers := []int{1, 2, 3, 4, 5}
common.Iterate(numbers, func(index int, value interface{}) {
    fmt.Printf("Index: %d, Value: %v\n", index, value)
})
// Output:
// Index: 0, Value: 1
// Index: 1, Value: 2
// Index: 2, Value: 3
// Index: 3, Value: 4
// Index: 4, Value: 5
7. Comparison and Type Checking
DeepEqual
// Compare via JSON serialization
type Person struct {
    Name string
    Age  int
}

p1 := Person{Name: "Alice", Age: 30}
p2 := Person{Name: "Alice", Age: 30}
p3 := Person{Name: "Bob", Age: 25}

fmt.Println(common.DeepEqual(p1, p2)) // true
fmt.Println(common.DeepEqual(p1, p3)) // false
DeepEqualComp
// Compare comparable types
slice1 := []int{1, 2, 3}
slice2 := []int{1, 2, 3}
slice3 := []int{1, 2, 4}

fmt.Println(common.DeepEqualComp(slice1, slice2)) // true
fmt.Println(common.DeepEqualComp(slice1, slice3)) // false
IsScalarType
fmt.Println(common.IsScalarType(42))          // true (int)
fmt.Println(common.IsScalarType("hello"))     // true (string)
fmt.Println(common.IsScalarType(3.14))        // true (float64)
fmt.Println(common.IsScalarType(true))        // true (bool)
fmt.Println(common.IsScalarType([]int{1,2}))  // false (slice)
fmt.Println(common.IsScalarType(nil))         // false (nil)
IsEmptyValue
import "reflect"

// Empty values
fmt.Println(common.IsEmptyValue(reflect.ValueOf("")))        // true
fmt.Println(common.IsEmptyValue(reflect.ValueOf(0)))         // true
fmt.Println(common.IsEmptyValue(reflect.ValueOf(false)))     // true
fmt.Println(common.IsEmptyValue(reflect.ValueOf([]int{})))   // true

// Non-empty values
fmt.Println(common.IsEmptyValue(reflect.ValueOf("hello")))   // false
fmt.Println(common.IsEmptyValue(reflect.ValueOf(42)))        // false
fmt.Println(common.IsEmptyValue(reflect.ValueOf([]int{1})))  // false
8. Practical Use Cases
Data Pipeline
// Multi-step data transformation
type User struct {
    Name   string
    Age    int
    Active bool
}

users := []User{
    {Name: "Alice", Age: 30, Active: true},
    {Name: "Bob", Age: 25, Active: false},
    {Name: "Charlie", Age: 35, Active: true},
}

// Convert to interface{} for common package
usersInterface := make([]interface{}, len(users))
for i, u := range users {
    usersInterface[i] = u
}

// Filter active users
active := common.Filter(usersInterface, func(v interface{}) bool {
    return v.(User).Active
})

// Extract names
names := common.Transform(active, func(v interface{}) interface{} {
    return v.(User).Name
})

fmt.Println(names) // ["Alice" "Charlie"]
Statistical Operations
numbers := []int{1, 2, 3, 4, 5}

// Convert to interface{}
numbersInterface := make([]interface{}, len(numbers))
for i, n := range numbers {
    numbersInterface[i] = n
}

// Calculate average
sum := common.Reduce(numbersInterface, func(acc interface{}, v interface{}) interface{} {
    return acc.(int) + v.(int)
}, 0).(int)

count := len(numbers)
average := float64(sum) / float64(count)
fmt.Printf("Average: %.2f\n", average) // Average: 3.00

API Reference

Functional Operations
Function Signature Description
Transform (collection any, fn func(any) any) any Apply function to each element
Filter (collection any, fn func(any) bool) any Keep elements matching condition
Reduce (collection any, fn func(any, any) any, init any) any Reduce to single value (left-to-right)
ReduceRight (collection any, fn func(any, any) any, init any) any Reduce to single value (right-to-left)
Iterate (collection any, fn func(int, any)) Iterate with callback
Collection Queries
Function Signature Description
Find (collection any, fn func(any) bool) any Find first matching element
All (collection any, fn func(any) bool) bool Check if all match condition
Any (collection any, fn func(any) bool) bool Check if any match condition
Count (collection any, fn func(any) bool) int Count matching elements
Contains (collection any, element any) bool Check if element exists
Collection Manipulation
Function Signature Description
Remove (collection any, fn func(any) bool) any Remove matching elements
Unique (collection any) any Remove duplicates
Reverse (collection any) Reverse in-place
Sort (collection any, fn func(i, j int) bool) Custom sort in-place
Set Operations
Function Signature Description
Difference (col1, col2 any) any Elements in col1 not in col2
Intersection (col1, col2 any) any Common elements
Slicing and Partitioning
Function Signature Description
Slice (collection any, start, end int) any Extract subrange
SliceWithIndices (collection any, indices []int) any Extract by indices
Partition (collection any, fn func(any) bool) (any, any) Split by condition
Zip (collections ...any) []any Combine collections
RotateLeft (collection any, positions int) any Rotate left
RotateRight (collection any, positions int) any Rotate right
Comparison and Type Checking
Function Signature Description
DeepEqual (a, b any) bool Compare via JSON serialization
DeepEqualComp [T comparable](a, b T) bool Deep equality for comparable types
IsScalarType (value any) bool Check if primitive type
IsEmptyValue (v reflect.Value) bool Check if reflect.Value is empty

Best Practices & Notes

⚠️ Common Pitfalls
  1. Type Assertions

    // ❌ Forgetting type assertion
    numbers := []int{1, 2, 3}
    result := common.Transform(numbers, func(v interface{}) interface{} {
        return v * v // Won't compile!
    })
    
    // ✅ Correct: type assert
    result := common.Transform(numbers, func(v interface{}) interface{} {
        return v.(int) * v.(int)
    })
    
  2. Panic on Wrong Type

    // ❌ Dangerous: will panic if type is wrong
    numbers := []interface{}{1, "two", 3}
    squared := common.Transform(numbers, func(v interface{}) interface{} {
        return v.(int) * v.(int) // Panics on "two"
    })
    
    // ✅ Safe: check type
    squared := common.Transform(numbers, func(v interface{}) interface{} {
        if num, ok := v.(int); ok {
            return num * num
        }
        return v // or handle error
    })
    
  3. Modifying Original Collections

    // ⚠️ Reverse modifies in-place
    numbers := []int{1, 2, 3}
    common.Reverse(numbers)
    fmt.Println(numbers) // [3 2 1] - original modified!
    
    // ⚠️ Sort modifies in-place
    common.Sort(numbers, func(i, j int) bool {
        return numbers[i] < numbers[j]
    })
    
  4. Performance Overhead

    // ❌ Slow for performance-critical code
    for i := 0; i < 1000000; i++ {
        result := common.Transform(data, transform)
    }
    
    // ✅ Use type-specific code or generics
    for i := 0; i < 1000000; i++ {
        for j := range data {
            result[j] = transform(data[j])
        }
    }
    
💡 Recommendations

Use for dynamic types

// When type is unknown at compile time
func ProcessData(data interface{}) {
    filtered := common.Filter(data, condition)
    // ...
}

Consider type-safe alternatives

// Prefer generics (Go 1.18+) when type is known
import "github.com/sivaosorg/replify/pkg/coll"

numbers := []int{1, 2, 3, 4, 5}
evens := coll.Filter(numbers, func(n int) bool {
    return n%2 == 0
}) // Type-safe, no assertions needed

Handle type assertion errors

result := common.Transform(data, func(v interface{}) interface{} {
    if num, ok := v.(int); ok {
        return num * 2
    }
    return nil // or default value
})

Document expected types

// ProcessNumbers expects a slice or array of integers
func ProcessNumbers(numbers interface{}) interface{} {
    return common.Filter(numbers, func(v interface{}) bool {
        return v.(int) > 0
    })
}

Check collection types before operations

if reflect.TypeOf(data).Kind() == reflect.Slice {
    result := common.Transform(data, fn)
}
🔒 Thread Safety

Not thread-safe by default. Functions that modify in-place (Reverse, Sort) should not be called concurrently on the same collection.

// ❌ Unsafe
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        common.Reverse(sharedSlice) // Race condition!
    }()
}

// ✅ Safe: use mutex or create copies
var mu sync.Mutex
mu.Lock()
common.Reverse(sharedSlice)
mu.Unlock()
⚡ Performance Considerations

Reflection is slow compared to type-specific operations:

// Benchmark comparison
// Type-specific:     ~1 ns/op
// Reflection-based:  ~100 ns/op (100x slower)

When to optimize:

  • Hot paths in your application
  • Large datasets (>10,000 elements)
  • Tight loops with repeated operations

Optimization strategies:

  1. Use generics instead (Go 1.18+)
  2. Write type-specific implementations
  3. Profile before optimizing
  4. Cache reflection results when possible
🐛 Debugging Tips

Print types:

fmt.Printf("Type: %T, Value: %v\n", value, value)

Check for nil:

if value == nil {
    fmt.Println("Value is nil")
}

Inspect reflection values:

v := reflect.ValueOf(data)
fmt.Printf("Kind: %v, Type: %v, Len: %d\n", v.Kind(), v.Type(), v.Len())
📝 Testing

Example tests:

func TestTransform(t *testing.T) {
    numbers := []int{1, 2, 3}
    result := common.Transform(numbers, func(v interface{}) interface{} {
        return v.(int) * 2
    })
    
    expected := []interface{}{2, 4, 6}
    if !common.DeepEqual(result, expected) {
        t.Errorf("Got %v, want %v", result, expected)
    }
}

Limitations

  • Runtime type checks: No compile-time type safety
  • Performance overhead: Reflection is slower than direct operations
  • Panic risk: Type assertions can panic if incorrect
  • No compile-time errors: Type mistakes discovered at runtime
  • Memory overhead: Interface{} boxes require heap allocations

When to Use vs. Generics

Use common when:

  • Type is unknown at compile time
  • Working with interface{} types
  • Need dynamic type handling
  • Building flexible libraries

Use generics when:

  • Type is known at compile time
  • Performance matters
  • Want type safety
  • Using Go 1.18+

Contributing

Contributions are welcome! Please see the main replify repository for contribution guidelines.

License

This library is part of the replify project.

Part of the replify ecosystem:

  • replify - API response wrapping library
  • conv - Type conversion utilities
  • coll - Type-safe collection utilities (generics)
  • hashy - Deterministic hashing
  • match - Wildcard pattern matching
  • strutil - String utilities
  • randn - Random data generation
  • encoding - JSON encoding utilities

Documentation

Overview

Package common provides shared runtime utilities used across the replify packages. It covers three broad areas: deep equality comparisons, I/O helper functions, and generic collection transformations.

Deep Equality

DeepEqualComp uses reflect.DeepEqual to compare two values of the same comparable type. DeepEqual serialises both values to JSON and compares the resulting byte slices, making it a convenient option for loosely typed comparisons where field ordering in maps is not a concern.

IsScalarType reports whether a value is one of Go's built-in primitive types (bool, numeric, or string). IsEmptyValue extends this to any reflect.Value, returning true when the value is a zero length collection, false boolean, zero numeric, nil pointer or interface, or zero struct.

I/O Helpers

ReadAll and SlurpAll both drain an io.Reader into a string; ReadAll delegates to io.Copy while SlurpAll reads in 1 KiB chunks. SlurpLines returns each line as a separate element of a string slice, and SlurpLine concatenates all lines into a single string.

TeeCopy reads from an io.Reader and writes to an io.Writer simultaneously, returning the data as a string. TeeTap is similar but discards the writer side, effectively logging a stream without forwarding it.

Generic Transformations

Transform applies a predicate to every element of a slice, array, or map using reflection, returning a new slice of the mapped values. A rich set of higher-order slice functions — Filter, Reduce, ForEach, GroupBy, Partition, Chunk, Unique, Flatten, ZipWith, Any, All, and None — round out the functional programming toolkit available to replify consumers.

All functions in this package are stateless and safe for concurrent use.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func All

func All(collection any, condition func(value any) bool) bool

All checks whether all elements in a collection (slice or array) satisfy a given condition.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice or array, and a condition function (`condition`). The condition function is applied to each element of the collection, and the function returns `true` if all elements satisfy the condition. If any element does not satisfy the condition, the function returns `false`. If the collection is empty, the function returns `true` (since the condition is trivially satisfied).

The function uses reflection to handle slices and arrays, iterating over the collection to check each element with the provided condition.

Parameters:

  • `collection`: The collection (slice or array) to check. It can be of any type, but only slices and arrays are supported.
  • `condition`: A function that takes a value from the collection and returns a boolean indicating whether the element satisfies the condition. If it returns `false` for any element, the function immediately returns `false`.

Returns:

  • `true` if all elements in the collection satisfy the condition, otherwise `false`.
  • If the collection is empty, the function returns `true`.

Example:

// Checking if all elements in a slice of integers are positive
numbers := []int{1, 2, 3, 4, 5}
allPositive := All(numbers, func(value interface{}) bool {
	return value.(int) > 0
})
// allPositive will be true

Notes:

  • This function only works with slices or arrays. If the collection is of another type, it will return `false`.
  • The function returns `false` as soon as it finds an element that does not satisfy the condition, making it more efficient for early termination.

Limitations:

  • The function works only with slices and arrays. If no elements satisfy the condition, the function will return `false`, but if all elements are valid, it will return `true`. An empty collection is considered to trivially satisfy the condition.

func Any

func Any(collection any, condition func(value any) bool) bool

Any checks whether any element in a collection (slice or array) satisfies a given condition.

This function accepts a collection (slice or array) and a condition function. It returns `true` if at least one element in the collection satisfies the condition, and `false` if none do. The function stops iterating as soon as a matching element is found, making it more efficient for early termination.

Parameters:

  • `collection`: A slice or array of any type to check.
  • `condition`: A function that takes a value from the collection and returns a boolean indicating whether the element satisfies the condition.

Returns:

  • `true` if any element satisfies the condition, `false` otherwise.

Example:

numbers := []int{1, 2, 3, 4, 5}
anyNegative := Any(numbers, func(value interface{}) bool {
  return value.(int) < 0
})
// anyNegative will be false because no element is negative.

func Contains

func Contains(collection any, element any) bool

Contains checks if a given element exists within a collection (slice or array).

This function takes a collection (slice or array) and an element, then iterates through the collection to see if any element matches the provided element. It uses `reflect.DeepEqual` to compare elements, which ensures that even complex types (like structs or slices) are compared correctly.

Parameters:

  • `collection`: A slice or array of any type to search within.
  • `element`: The element to search for within the collection.

Returns:

  • `true` if the element is found within the collection, `false` otherwise.

Example:

numbers := []int{1, 2, 3, 4, 5}
containsThree := Contains(numbers, 3)
// containsThree will be true because 3 is in the slice.

func Count

func Count(collection any, condition func(value any) bool) int

Count returns the number of elements in a collection (slice or array) that satisfy a given condition.

This function takes a collection (slice or array) and a condition function. It iterates through the collection, applying the condition to each element. It returns the total count of elements that satisfy the condition.

Parameters:

  • `collection`: A slice or array of any type to check.
  • `condition`: A function that checks if an element satisfies a condition. The function returns `true` for elements that match the condition, and `false` otherwise.

Returns:

  • The count of elements in the collection that satisfy the condition.

Example:

numbers := []int{1, 2, 3, 4, 5}
countNegative := Count(numbers, func(value interface{}) bool {
  return value.(int) < 0
})
// countNegative will be 0, since no element is negative.

func DeepEqual

func DeepEqual(a, b any) bool

DeepEqual compares two values for equality via JSON serialization.

This function serializes both input values `a` and `b` to JSON format using the `json.Marshal` function. It then compares the resulting JSON byte slices using `bytes.Equal`. If the serialized JSON representations are identical, the function returns `true`, indicating that the two values are considered equal in terms of their JSON representation.

Parameters:

  • `a`: The first value to compare. It can be of any type.
  • `b`: The second value to compare. It can also be of any type.

Returns:

  • `true` if the JSON representations of `a` and `b` are equal; `false` otherwise.

Example:

// Comparing two structs with the same data
type Person struct {
    Name string
    Age  int
}
personA := Person{Name: "Alice", Age: 30}
personB := Person{Name: "Alice", Age: 30}
isEqual := DeepEqual(personA, personB)
// isEqual will be true as both structs serialize to the same JSON
// Comparing two different maps
mapA := map[string]int{"a": 1, "b": 2}
mapB := map[string]int{"a": 1, "b": 3}
isEqual = DeepEqual(mapA, mapB)
// isEqual will be false as the JSON representations differ

func DeepEqualComp

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

DeepEqualComp compares two values of any comparable type to determine if they are deeply equal.

This function uses the `reflect.DeepEqualComp` function from the `reflect` package to compare two values `a` and `b`. It checks for deep equality, meaning it considers nested structures, such as slices, maps, or structs, and compares them element-by-element or field-by-field. If the values are deeply equal, the function returns `true`; otherwise, it returns `false`.

The function is generic, allowing it to work with any type `T` that is comparable, including basic types (e.g., integers, strings) as well as complex types with nested structures.

Parameters:

  • `a`: The first value to compare. It can be of any comparable type `T`.
  • `b`: The second value to compare. It must be of the same type `T` as `a`.

Returns:

  • `true` if `a` and `b` are deeply equal; `false` otherwise.

Example:

// Comparing two integer values
isEqual := DeepEqualComp(5, 5)
// isEqual will be true as both integers are equal

// Comparing two slices with the same elements
sliceA := []int{1, 2, 3}
sliceB := []int{1, 2, 3}
isEqual = DeepEqualComp(sliceA, sliceB)
// isEqual will be true as both slices have identical elements in the same order

// Comparing two different maps
mapA := map[string]int{"a": 1, "b": 2}
mapB := map[string]int{"a": 1, "b": 3}
isEqual = DeepEqualComp(mapA, mapB)
// isEqual will be false as the values for key "b" differ between the maps

func Difference

func Difference(collection1 any, collection2 any) any

Difference returns a new collection (slice or array) containing elements from the first collection that are not present in the second collection.

This function takes two collections (slices or arrays) and compares the elements of the first collection against the elements of the second collection. It returns a new collection with elements that appear in the first collection but are absent in the second collection. The function uses `Contains_N` to check for membership of each element of the first collection in the second collection.

Parameters:

  • `collection1`: The first slice or array of any type to compare.
  • `collection2`: The second slice or array of any type to compare against.

Returns:

  • A new collection (slice or array) containing the elements from `collection1` that are not in `collection2`.

Example:

numbers1 := []int{1, 2, 3, 4, 5}
numbers2 := []int{3, 4, 6}
result := Difference(numbers1, numbers2)
// result will be []int{1, 2, 5}, as these are the elements in numbers1 that are not in numbers2.

func Filter

func Filter(collection any, predicate func(value any) bool) any

Filter filters a collection (slice or array) based on a predicate function and returns a new collection containing only the elements that satisfy the condition specified by the predicate.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice or array, and a filtering predicate function (`predicate`). The function applies the `predicate` function to each element in the collection, and if the predicate returns true, the element is included in the new collection. The function only supports slices and arrays as input coll.

The function uses reflection to handle slices and arrays and constructs a new collection containing only the elements that pass the filter condition.

Parameters:

  • `collection`: The collection (slice or array) to filter. It can be of any type, but only slices and arrays are supported.
  • `predicate`: A function that takes a value from the collection and returns a boolean indicating whether the element should be included in the resulting collection. If it returns true, the element is included.

Returns:

  • A new collection of the same type as the original collection, containing only the elements that satisfy the condition defined by the `predicate` function.

Example:

// Filtering a slice of integers to get only even numbers
numbers := []int{1, 2, 3, 4, 5, 6}
evens := Filter(numbers, func(value interface{}) bool {
	return value.(int)%2 == 0
})
// evens will be []int{2, 4, 6}

Notes:

  • This function only works with slices or arrays, and will return an empty collection if the input is of another type.

Limitations:

  • The function creates a new collection based on the results of the `predicate` function, so it does not modify the original collection.

func Find

func Find(collection any, predicate func(value any) bool) any

Find searches for the first element in a collection (slice or array) that satisfies a given predicate function and returns it.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice or array, and a predicate function (`predicate`). The predicate function is applied to each element of the collection, and if it returns true for an element, that element is returned as the result. The function will return the first element that satisfies the condition, and if no elements satisfy the condition, it returns `nil`.

The function uses reflection to handle slices and arrays, iterating over the collection to check each element with the provided predicate.

Parameters:

  • `collection`: The collection (slice or array) to search through. It can be of any type, but only slices and arrays are supported.
  • `predicate`: A function that takes a value from the collection and returns a boolean indicating whether the element satisfies the condition. If it returns true, the element is returned.

Returns:

  • The first element from the collection that satisfies the `predicate` function. If no elements satisfy the condition, it returns `nil`.

Example:

// Finding the first even number in a slice of integers
numbers := []int{1, 2, 3, 4, 5}
even := Find(numbers, func(value interface{}) bool {
	return value.(int)%2 == 0
})
// even will be 2 (the first even number)

Notes:

  • This function only works with slices or arrays. If the collection is of another type, it will return `nil`.
  • The function returns the first element that matches the predicate and stops searching after finding it.

Limitations:

  • The function works only with slices and arrays. If no elements satisfy the predicate, the function will return `nil`, even if the collection is non-empty.

func Intersection

func Intersection(collection1 any, collection2 any) any

Intersection returns a new collection (slice or array) containing elements that are present in both coll.

This function takes two collections (slices or arrays) and compares the elements of both coll. It returns a new collection with elements that appear in both the first and the second collection. The function uses `Contains_N` to check if an element from the first collection is also present in the second collection.

Parameters:

  • `collection1`: The first slice or array of any type to compare.
  • `collection2`: The second slice or array of any type to compare against.

Returns:

  • A new collection (slice or array) containing the elements that are found in both `collection1` and `collection2`.

Example:

numbers1 := []int{1, 2, 3, 4, 5}
numbers2 := []int{3, 4, 6}
result := Intersection(numbers1, numbers2)
// result will be []int{3, 4}, as these are the elements common to both numbers1 and numbers2.

func IsEmptyValue

func IsEmptyValue(v reflect.Value) bool

IsEmptyValue checks whether the given reflect.Value is considered empty.

An empty value is defined as:

  • Zero length for arrays, maps, slices, and strings.
  • False for booleans.
  • Zero for numeric types (int, uint, float).
  • Nil for interfaces and pointers.
  • Zero value for structs.

Parameters:

  • v: The reflect.Value to check.

Returns:

  • true if the value is empty, false otherwise.

Example:

val := reflect.ValueOf("")
if IsEmptyValue(val) {
    fmt.Println("The value is empty.")
} else {
    fmt.Println("The value is not empty.")
}

func IsScalarType

func IsScalarType(value any) bool

IsScalarType checks whether the given value is a primitive type in Go.

Primitive types include:

  • Signed integers: int, int8, int16, int32, int64
  • Unsigned integers: uint, uint8, uint16, uint32, uint64, uintptr
  • Floating-point numbers: float32, float64
  • Complex numbers: complex64, complex128
  • Boolean: bool
  • String: string

The function first checks if the input value is `nil`, returning `false` if so. Otherwise, it uses reflection to determine the type of the value and compares it against known primitive types.

Parameters:

  • `value`: An interface{} that can hold any Go value. The function checks the type of this value.

Returns:

  • `true` if the value is of a primitive type.
  • `false` if the value is `nil` or not a primitive type.

Example:

primitive := 42
if IsScalarType(primitive) {
    fmt.Println("The value is a primitive type.")
} else {
    fmt.Println("The value is not a primitive type.")
}

func Iterate

func Iterate(collection any, callback func(index int, value any))

Iterate iterates over a collection (slice, array, or map) and applies a callback function on each element.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice, array, or map, and a callback function. The callback function is executed for each element in the collection. For slices and arrays, the callback receives the index and the corresponding value. For maps, the callback receives each key and value, with the key being passed first followed by the value. For slices and arrays, the index is passed, while for maps, it is passed as -1 (since maps are unordered).

The function uses reflection to handle different types of collections and ensures that the correct value is passed to the callback.

Parameters:

  • `collection`: The collection (slice, array, or map) to iterate over. It can be of any type.
  • `callback`: A function that takes two arguments: the index (for slices and arrays, -1 for maps) and the value from the collection. The callback is executed for each element in the collection.

Example:

// Iterating over a slice
numbers := []int{1, 2, 3, 4}
Iterate(numbers, func(index int, value interface{}) {
	fmt.Printf("Index: %d, Value: %v\n", index, value)
})
// Output:
// Index: 0, Value: 1
// Index: 1, Value: 2
// Index: 2, Value: 3
// Index: 3, Value: 4

// Iterating over a map
colors := map[string]string{"red": "FF0000", "green": "00FF00", "blue": "0000FF"}
Iterate(colors, func(index int, value interface{}) {
	fmt.Printf("Value: %v\n", value)
})
// Output:
// Value: red
// Value: FF0000
// Value: green
// Value: 00FF00
// Value: blue
// Value: 0000FF

Notes:

  • For slices and arrays, the callback will receive the index and the value from the collection.
  • For maps, the callback will be executed twice per key-value pair: once with the key and once with the value, since maps are unordered and the order of key-value pairs cannot be guaranteed.

func Partition

func Partition(collection any, condition func(value any) bool) (any, any)

Partition splits a collection (slice or array) into two parts based on a condition function.

This function iterates through the elements of the input collection, applying the provided `condition` function to each element. It creates two separate collections: one containing elements for which the condition returns `true`, and the other containing elements for which the condition returns `false`. The function returns both coll.

Parameters:

  • `collection`: A slice or array of any type to partition.
  • `condition`: A function that checks whether an element should go into the "true" partition or the "false" partition. It returns `true` if the element should go into the "true" partition, and `false` otherwise.

Returns:

  • A tuple containing two new collections:
  • The first collection contains elements for which the condition is `true`.
  • The second collection contains elements for which the condition is `false`.

Example:

numbers := []int{1, 2, 3, 4, 5, 6}
truePartition, falsePartition := Partition(numbers, func(value interface{}) bool {
	return value.(int) % 2 == 0 // Partition into even and odd numbers
})
// truePartition will be []int{2, 4, 6} (even numbers)
// falsePartition will be []int{1, 3, 5} (odd numbers)

Notes:

  • The condition function is applied to each element in the collection.
  • The returned collections are of the same type as the input collection (slice or array).

func ReadAll

func ReadAll(in io.Reader) (string, error)

ReadAll reads all data from an io.Reader and returns it as a single string.

This function uses an `io.Copy` operation to efficiently read data from the provided `io.Reader` and write it to a `bytes.Buffer`. The resulting buffer content is then converted to a string and returned.

Parameters:

  • in: An `io.Reader` from which the function will read data. This can be any type that implements the `io.Reader` interface, such as a file, standard input, or a network connection.

Returns:

  • A string containing all the data read from the `io.Reader`.
  • An error if any I/O operation fails during the copy process. If the input is successfully read until EOF, the error returned is `nil`.

Details:

  • The function creates a `bytes.Buffer` to store the data read from the `io.Reader`.
  • It uses the `io.Copy` function to transfer data from the `io.Reader` to the `bytes.Buffer`. This approach is simple and efficient, leveraging built-in Go utilities for stream copying.
  • After copying is complete, the data in the buffer is converted to a string and returned.

Example Usage:

// Example: Reading from a file
file, err := os.Open("example.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

content, err := ReadAll(file)
if err != nil {
    log.Fatal(err)
}
fmt.Println(content)

// Example: Reading from standard input
fmt.Println("Enter some text (press Ctrl+D to end):")
content, err = ReadAll(os.Stdin)
if err != nil {
    log.Fatal(err)
}
fmt.Println("You entered:")
fmt.Println(content)

func Reduce

func Reduce(collection any, reducer func(acc any, value any) any, initialValue any) any

Reduce reduces a collection (slice or array) to a single value by applying a reducer function to each element, combining them into a single result.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice or array, and a reducer function (`reducer`). The reducer function is applied to each element of the collection, along with an accumulator value, and the result of the reducer is passed as the accumulator to the next iteration. This process continues until all elements are processed, resulting in a single accumulated value.

The function uses reflection to handle slices and arrays and iterates over the collection to apply the reducer function.

Parameters:

  • `collection`: The collection (slice or array) to reduce. It can be of any type, but only slices and arrays are supported.
  • `reducer`: A function that takes two arguments: an accumulator and an element from the collection. It returns a new accumulator value after combining the accumulator and the element.
  • `initialValue`: The initial value of the accumulator, used as the starting point for the reduction process.

Returns:

  • A single value, which is the result of reducing the entire collection using the `reducer` function. The return type is the same as the type of the `initialValue`.

Example:

// Reducing a slice of integers by summing them
numbers := []int{1, 2, 3, 4, 5}
sum := Reduce(numbers, func(acc interface{}, value interface{}) interface{} {
	return acc.(int) + value.(int)
}, 0)
// sum will be 15

Notes:

  • This function only works with slices or arrays, and will return the initial value if the input collection is empty.

Limitations:

  • The function creates a single accumulated result by repeatedly applying the `reducer` function to each element, so it does not modify the original collection.

func ReduceRight

func ReduceRight(collection any, reducer func(acc, value any) any, initialValue any) any

ReduceRight performs a right-to-left reduction on a collection (slice or array) using a reducer function.

This function takes a collection (slice or array), a reducer function, and an initial accumulator value. It iterates through the collection from right to left, applying the reducer function to each element and accumulating the result. The reducer function is called with two arguments: the current accumulator value and the current element in the collection. The final result is returned after processing all elements.

Parameters:

  • `collection`: A slice or array of any type to reduce.
  • `reducer`: A function that takes the current accumulator value and the current element as input, and returns the updated accumulator value. This function is applied from right to left on the collection.
  • `initialValue`: The initial value for the accumulator, which will be passed as the first argument to the reducer function during the first iteration.

Returns:

  • The final accumulated value after reducing the collection from right to left.

Example:

numbers := []int{1, 2, 3, 4}
result := ReduceRight(numbers, func(acc, value interface{}) interface{} {
	return acc.(int) + value.(int) // Sum of elements from right to left
}, 0)
// result will be 10, as the reduction is (0 + 4) + (4 + 3) + (7 + 2) + (9 + 1) = 10

Notes:

  • The reduction starts from the rightmost element of the collection and proceeds towards the left.
  • The function uses reflection to support collections of any type.

func Remove

func Remove(collection any, condition func(value any) bool) any

Remove returns a new collection (slice or array) where all elements that satisfy a given condition are removed.

This function takes a collection (slice or array) and a condition function. It iterates through the collection, and for each element that does not satisfy the condition, it is added to a new result collection. The function returns a new collection that contains only the elements that do not match the condition.

Parameters:

  • `collection`: A slice or array of any type to process.
  • `condition`: A function that checks if an element satisfies a condition. If the element matches the condition, it is removed from the result collection.

Returns:

  • A new collection (slice or array) with elements that do not satisfy the condition.

Example:

numbers := []int{1, 2, 3, 4, 5}
result := Remove(numbers, func(value interface{}) bool {
  return value.(int) % 2 == 0 // Removes even numbers
})
// result will be []int{1, 3, 5}

func Reverse

func Reverse(collection any)

Reverse reverses the order of elements in a collection (slice or array) in-place.

This function takes a collection (slice or array) and reverses the elements by swapping elements at corresponding positions (i and length-i-1) until the middle of the collection is reached. The collection is modified in place, meaning no new collection is created.

Parameters:

  • `collection`: A slice or array of any type to reverse.

Returns:

  • The collection is reversed in place. No value is returned.

Example:

numbers := []int{1, 2, 3, 4, 5}
Reverse(numbers)
// numbers will be reversed to []int{5, 4, 3, 2, 1}

func RotateLeft

func RotateLeft(collection any, positions int) any

RotateLeft rotates the elements of a collection (slice or array) to the left by a specified number of positions.

This function takes a collection (slice or array) and rotates its elements to the left by the given number of positions. Elements at the beginning of the collection are moved to the end. The rotation is done in-place (modifying the collection), and if the number of positions is negative, the rotation will be adjusted accordingly.

Parameters:

  • `collection`: A slice or array of any type to rotate.
  • `positions`: The number of positions to rotate the collection to the left. A positive number rotates elements left, while a negative number rotates right. The positions are normalized to be within the valid range of the collection's length.

Returns:

  • A new collection (slice or array) where the elements have been rotated left by the specified number of positions.

Example:

numbers := []int{1, 2, 3, 4, 5}
result := RotateLeft(numbers, 2)
// result will be []int{3, 4, 5, 1, 2}, as the collection is rotated left by 2 positions.

Notes:

  • If the number of positions is larger than the length of the collection, it is normalized using modulo to ensure it rotates only the necessary number of positions.
  • If the collection is not a slice or array, the original collection is returned unchanged.

func RotateRight

func RotateRight(collection any, positions int) any

RotateRight rotates the elements of a collection (slice or array) to the right by a specified number of positions.

This function takes a collection (slice or array) and rotates its elements to the right by the given number of positions. Elements at the end of the collection are moved to the beginning. The rotation is done in-place (modifying the collection), and if the number of positions is negative, the rotation will be adjusted accordingly.

Parameters:

  • `collection`: A slice or array of any type to rotate.
  • `positions`: The number of positions to rotate the collection to the right. A positive number rotates elements right, while a negative number rotates left. The positions are normalized to be within the valid range of the collection's length.

Returns:

  • A new collection (slice or array) where the elements have been rotated right by the specified number of positions.

Example:

numbers := []int{1, 2, 3, 4, 5}
result := RotateRight(numbers, 2)
// result will be []int{4, 5, 1, 2, 3}, as the collection is rotated right by 2 positions.

Notes:

  • If the number of positions is larger than the length of the collection, it is normalized using modulo to ensure it rotates only the necessary number of positions.
  • If the collection is not a slice or array, the original collection is returned unchanged.

func Slice

func Slice(collection any, start, end int) any

Slice returns a new collection (slice or array) that is a sub-range of the input collection, starting from the specified `start` index and ending at the `end` index (exclusive).

This function creates a sub-slice or sub-array from the given collection by copying elements from the original collection starting at the `start` index and ending just before the `end` index. If `start` or `end` is out of bounds, the function adjusts them to ensure they stay within the valid range for the collection. The returned collection will contain the elements between the adjusted `start` and `end` indices.

Parameters:

  • `collection`: A slice or array of any type to extract a subrange from.
  • `start`: The starting index (inclusive) for the sub-slice or sub-array.
  • `end`: The ending index (exclusive) for the sub-slice or sub-array.

Returns:

  • A new collection (slice or array) containing the elements from `start` to `end` (exclusive).

Example:

numbers := []int{1, 2, 3, 4, 5, 6, 7}
result := Slice(numbers, 2, 5)
// result will be []int{3, 4, 5}, as it extracts elements from index 2 to 4 (inclusive of 2, exclusive of 5).

Notes:

  • If `start` is less than 0, it is adjusted to 0 (beginning of the collection).
  • If `end` is greater than the length of the collection, it is adjusted to the collection's length.

func SliceWithIndices

func SliceWithIndices(collection any, indices []int) any

SliceWithIndices returns a new collection (slice or array) containing elements from the input collection specified by the provided indices.

This function creates a new collection by selecting elements from the original collection based on a list of indices provided in the `indices` slice. It ensures that only valid indices (within the bounds of the collection) are used to build the result collection.

Parameters:

  • `collection`: A slice or array of any type to extract elements from.
  • `indices`: A slice of integers representing the indices of elements to include in the result collection. Only valid indices (within the bounds of the collection) are considered.

Returns:

  • A new collection (slice or array) containing the elements from `collection` at the specified `indices`.

Example:

numbers := []int{1, 2, 3, 4, 5, 6, 7}
indices := []int{1, 3, 5}
result := SliceWithIndices(numbers, indices)
// result will be []int{2, 4, 6}, as these are the elements at indices 1, 3, and 5 of the original slice.

Notes:

  • If an index in `indices` is out of bounds (less than 0 or greater than or equal to the collection's length), it is ignored.

func SlurpAll

func SlurpAll(in io.Reader) (string, error)

SlurpAll reads all data from an io.Reader and returns it as a single string.

This function reads raw bytes from the provided `io.Reader` in chunks and appends them to a buffer until EOF (end of file) is reached.

Parameters:

  • in: An `io.Reader` from which the function will read data. This could be any type that implements the `io.Reader` interface, such as a file, standard input, or a network connection.

Returns:

  • A string containing all the data read from the `io.Reader`.
  • An error if any I/O operation fails (other than reaching EOF). If the input is successfully read until EOF, the error returned is `nil`.

Details:

  • The function creates a buffer of size 1024 bytes to read chunks of data from the `io.Reader`. This approach avoids reading the entire input into memory at once and is suitable for handling large streams of data.
  • The data read from each chunk is written to a `bytes.Buffer`, which efficiently constructs the final string.
  • If EOF is encountered during reading, the loop ends, and the accumulated data in the buffer is returned as a string.
  • Any other error during reading results in an immediate return of the error.

Example Usage:

// Example: Reading from a file
file, err := os.Open("example.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

content, err := SlurpAll(file)
if err != nil {
    log.Fatal(err)
}
fmt.Println(content)

// Example: Reading from standard input
fmt.Println("Enter some text (press Ctrl+D to end):")
content, err = SlurpAll(os.Stdin)
if err != nil {
    log.Fatal(err)
}
fmt.Println("You entered:")
fmt.Println(content)

func SlurpLine

func SlurpLine(in io.Reader) (string, error)

SlurpLine reads all lines from an io.Reader and returns them as a single string.

This function utilizes a buffered reader to read data line by line until EOF (end of file) is reached. It appends each line to a `strings.Builder`, which efficiently constructs the resulting string.

Parameters:

  • in: An `io.Reader` from which the function will read data. This could be any type that implements the `io.Reader` interface, such as a file, standard input, or a network connection.

Returns:

  • A string containing all the lines read from the `io.Reader`, concatenated together.
  • An error if any I/O operation fails (other than reaching EOF). If the input is successfully read until EOF, the error returned is `nil`.

Details:

  • The function uses `bufio.NewReader` to create a buffered reader around the provided `io.Reader`. This allows efficient reading of large input streams.
  • It reads lines one by one using `ReadString('\n')`. If a line does not end with a newline character before reaching EOF, the remaining text is still included in the result.
  • If an error occurs during reading (other than EOF), it stops reading and returns the error along with an empty string.

Example Usage:

// Example: Reading from a file
file, err := os.Open("example.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

content, err := SlurpLine(file)
if err != nil {
    log.Fatal(err)
}
fmt.Println(content)

// Example: Reading from standard input
fmt.Println("Enter some text:")
content, err = SlurpLine(os.Stdin)
if err != nil {
    log.Fatal(err)
}
fmt.Println("You entered:", content)

SlurpLine reads all lines from an io.Reader and returns them as a single string.

func SlurpLines

func SlurpLines(in io.Reader) ([]string, error)

SlurpLines reads all lines from an io.Reader and returns them as a slice of strings.

This function utilizes a buffered reader to read data line by line until EOF (end of file) is reached. Each line is appended to a slice of strings.

Parameters:

  • in: An `io.Reader` from which the function will read data. This can be any type that implements the `io.Reader` interface, such as a file, standard input, or a network connection.

Returns:

  • A slice of strings, where each element corresponds to a line read from the `io.Reader`.
  • An error if any I/O operation fails (other than reaching EOF). If the input is successfully read until EOF, the error returned is `nil`.

Details:

  • The function uses `bufio.NewReader` to create a buffered reader around the provided `io.Reader`, allowing efficient reading of large input streams.
  • It reads lines one by one using `ReadString('\n')`. Each line is appended to a slice of strings. If a line does not end with a newline character before reaching EOF, the remaining text is included as the last line in the slice.
  • If an error occurs during reading (other than EOF), it stops reading and returns the error along with the slice of lines read so far.

Example Usage:

// Example: Reading from a file
file, err := os.Open("example.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

lines, err := SlurpLines(file)
if err != nil {
    log.Fatal(err)
}
for i, line := range lines {
    fmt.Printf("Line %d: %s", i+1, line)
}

// Example: Reading from standard input
fmt.Println("Enter some text (press Ctrl+D to end):")
lines, err = SlurpLines(os.Stdin)
if err != nil {
    log.Fatal(err)
}
fmt.Println("You entered:")
for _, line := range lines {
    fmt.Println(line)
}

func Sort

func Sort(collection any, less func(i, j int) bool)

Sort sorts a collection (slice or array) in-place according to a custom comparison function.

This function takes a collection (slice or array) and a `less` function. The `less` function should return `true` if the element at index `i` should come before the element at index `j`. The collection is sorted in-place, meaning the original collection is modified.

Parameters:

  • `collection`: A slice or array of any type to sort.
  • `less`: A comparison function that takes two indices `i` and `j` and returns `true` if the element at index `i` should come before the element at index `j`.

Returns:

  • The collection is sorted in place. No value is returned.

Example:

numbers := []int{5, 3, 1, 4, 2}
Sort(numbers, func(i, j int) bool {
  return numbers[i] < numbers[j] // Sort in ascending order
})
// numbers will be sorted to []int{1, 2, 3, 4, 5}

func TeeCopy

func TeeCopy(in io.Reader, out io.Writer) (string, error)

TeeCopy reads data from an io.Reader and writes the data simultaneously to an io.Writer, while also returning the logged data as a string.

This function utilizes `io.TeeReader` to duplicate the data read from the provided `io.Reader`. A copy of the data is written to the provided `io.Writer`, while another copy is logged into a `bytes.Buffer`.

Parameters:

  • in: An `io.Reader` from which data will be read.
  • out: An `io.Writer` to which the read data will be written.

Returns:

  • A string containing all the data read from the `io.Reader` as logged in the `bytes.Buffer`.
  • An error if any I/O operation fails.

Details:

  • The function creates a `bytes.Buffer` to log a copy of the data.
  • It uses `io.TeeReader` to split the data read from `in` between logging to the buffer and passing the data to the consumer (`out`).
  • A loop is used to read data in chunks and write it to the `io.Writer`.
  • The function ensures that both reading and writing are handled efficiently.

Example Usage:

// Example: Reading from a file and writing to another file while logging
inFile, err := os.Open("input.txt")
if err != nil {
    log.Fatal(err)
}
defer inFile.Close()

outFile, err := os.Create("output.txt")
if err != nil {
    log.Fatal(err)
}
defer outFile.Close()

loggedData, err := TeeCopy(inFile, outFile)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Logged Data:")
fmt.Println(loggedData)

// Example: Reading from standard input and writing to standard output
fmt.Println("Enter some text (press Ctrl+D to end):")
loggedData, err = TeeCopy(os.Stdin, os.Stdout)
if err != nil {
    log.Fatal(err)
}
fmt.Println("\nLogged Data:")
fmt.Println(loggedData)

func TeeTap

func TeeTap(in io.Reader) (string, error)

TeeTap reads data from an io.Reader and logs the data simultaneously, returning the logged data as a string.

This function utilizes `io.TeeReader` to split the data read from the provided `io.Reader`. The `io.TeeReader` writes a copy of all read data into a `bytes.Buffer` while continuing to pass the data through as it is read. The function consumes all data from the `io.Reader` but discards it, keeping only the logged copy.

Parameters:

  • in: An `io.Reader` from which the function will read data. This can be any type that implements the `io.Reader` interface, such as a file, standard input, or a network connection.

Returns:

  • A string containing all the data read from the `io.Reader` as logged in the `bytes.Buffer`.
  • An error if any I/O operation fails (other than reaching EOF). If the input is successfully read until EOF, the error returned is `nil`.

Details:

  • The function creates a `bytes.Buffer` to store a copy of the data being read from the `io.Reader`.
  • An `io.TeeReader` is used to duplicate the data read: one copy is written to the buffer (`logs`), and the other is read into a temporary buffer to consume the input stream.
  • The function uses a loop to read from the `io.TeeReader` in chunks of 256 bytes until EOF is reached.
  • The accumulated data in the `bytes.Buffer` is returned as a string after the reading is complete.

Example Usage:

// Example: Reading from a file and logging data
file, err := os.Open("example.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

loggedData, err := TeeTap(file)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Logged Data:")
fmt.Println(loggedData)

// Example: Reading from standard input
fmt.Println("Enter some text (press Ctrl+D to end):")
loggedData, err = TeeTap(os.Stdin)
if err != nil {
    log.Fatal(err)
}
fmt.Println("You entered (logged):")
fmt.Println(loggedData)

func Transform

func Transform(collection any, predicate func(value any) any) any

Transform applies a transformation function to each element of a collection (slice, array, or map) and returns a new collection with the transformed values.

This function takes a collection of any type (using an empty `interface{}`), which can be a slice, array, or map, and a mapping function (`mapper`). The function applies the `mapper` function to each value in the collection, transforming it. For slices and arrays, the callback is applied to each element. For maps, the callback is applied to each key and value. The transformed elements (or key-value pairs) are collected into a new result, which is returned.

The function uses reflection to handle different types of collections and constructs a new collection with the transformed values.

Parameters:

  • `collection`: The collection (slice, array, or map) to iterate over and transform. It can be of any type.
  • `mapper`: A function that takes a value from the collection and transforms it. The function is applied to each element of the collection (or key-value pair for maps).

Returns:

  • A new collection of the same type as the original collection, where each element has been transformed using the provided `mapper` function.

Example:

// Mapping a slice of integers to their squares
numbers := []int{1, 2, 3, 4}
squared := Transform(numbers, func(value interface{}) interface{} {
	return value.(int) * value.(int)
})
// squared will be []int{1, 4, 9, 16}

// Mapping a map of strings to their lengths
words := map[string]string{"apple": "fruit", "carrot": "vegetable"}
lengths := Transform(words, func(value interface{}) interface{} {
	return len(value.(string))
})
// lengths will be a new collection containing key-value pairs, where values represent string lengths.

Notes:

  • For slices and arrays, the `mapper` function is applied to each element.
  • For maps, the `mapper` function is applied to both the key and the value. The resulting collection will include both transformed keys and values.

Limitations:

  • The function creates a new collection based on the results of the `mapper` function, so it does not modify the original collection.

func Unique

func Unique(collection any) any

Unique returns a new collection (slice or array) containing only unique elements from the original collection.

This function takes a collection (slice or array) and removes duplicate elements, returning a new collection with only the unique elements. The function uses a map to track elements that have already been encountered, ensuring that only the first occurrence of each element is included in the result.

Parameters:

  • `collection`: A slice or array of any type from which duplicates should be removed.

Returns:

  • A new collection (slice or array) containing only the unique elements from the original collection.

Example:

numbers := []int{1, 2, 2, 3, 4, 4, 5}
result := Unique(numbers)
// result will be []int{1, 2, 3, 4, 5}

func Zip

func Zip(collections ...any) []any

Zip combines multiple collections (slices or arrays) element-wise into a new collection of tuples.

This function takes multiple collections (slices or arrays) as arguments and combines their elements into tuples. Each tuple consists of elements from the same index of each collection. The function returns a new collection (a slice of tuples), where each tuple contains the elements from the input collections at the same position. The length of the resulting collection is determined by the shortest input collection.

Parameters:

  • `collections`: A variadic list of slices or arrays of any type to combine. All collections must be of the same length or shorter, and they will be combined element-wise until the shortest collection's length is reached.

Returns:

  • A slice of tuples (slices), where each tuple contains the elements from the input collections at the same index. The length of the returned slice is the same as the shortest input collection.

Example:

numbers := []int{1, 2, 3}
strings := []string{"a", "b", "c"}
result := Zip(numbers, strings)
// result will be [][]interface{}{{1, "a"}, {2, "b"}, {3, "c"}}

Notes:

  • If any of the input collections is not a slice or array, the function returns `nil`.
  • If the collections have different lengths, the function will combine elements up to the length of the shortest collection.

Types

This section is empty.

Jump to

Keyboard shortcuts

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