compare

package
v1.2.2 Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2026 License: MIT Imports: 0 Imported by: 1

README

compare

Comparison utilities and constraints for Go

The compare package provides comparison functions, constraints, and predicates for generic programming. It defines the Ordered constraint and offers utilities for comparing, sorting, and filtering.

Quick Reference

By Category:

Installation

go get github.com/modfin/henry/compare

Usage

import "github.com/modfin/henry/compare"

// Compare values
result := compare.Compare(5, 10)  // -1 (5 < 10)
result = compare.Compare(10, 10)    // 0 (equal)
result = compare.Compare(10, 5)      // 1 (10 > 5)

// Check range
inRange := compare.Between(5, 1, 10)  // true

// Create predicates
isEven := func(n int) bool { return n%2 == 0 }
isOdd := compare.NegateOf(isEven)

Core Comparisons

Compare

Three-way comparison with NaN handling.

// Standard comparison
compare.Compare(5, 10)   // -1
compare.Compare(10, 5)   // 1
compare.Compare(5, 5)    // 0

// With NaN (floats)
compare.Compare(math.NaN(), 5.0)      // -1 (NaN < everything)
compare.Compare(5.0, math.NaN())      // 1
twoNaNs := compare.Compare(math.NaN(), math.NaN()) // 0 (NaN == NaN for comparison)
Less / LessOrEqual
compare.Less(5, 10)          // true
compare.LessOrEqual(5, 5)    // true

// Use with sort
sorted := slicez.SortBy(nums, compare.Less[int])
Greater / GreaterOrEqual
compare.Greater(10, 5)          // true
compare.GreaterOrEqual(5, 5)    // true
Equal
compare.Equal(5, 5)    // true
compare.Equal(5, 3)    // false

Range Operations

Between

Check if value is in range.

// Inclusive (default)
compare.Between(5, 1, 10)                    // true (1 <= 5 <= 10)
compare.Between(1, 1, 10)                    // true
compare.Between(10, 1, 10)                   // true

// Exclusive
compare.Between(5, 1, 10, compare.BetweenExclusive)    // true (1 < 5 < 10)
compare.Between(1, 1, 10, compare.BetweenExclusive)    // false

// Left-inclusive
compare.Between(1, 1, 10, compare.BetweenLeftInclusive)   // true (1 <= 1 < 10)
compare.Between(10, 1, 10, compare.BetweenLeftInclusive) // false

// Right-inclusive
compare.Between(10, 1, 10, compare.BetweenRightInclusive) // true (1 < 10 <= 10)
Clamp

Constrain value to range.

compare.Clamp(50, 0, 100)    // 50 (within range)
compare.Clamp(-10, 0, 100)   // 0 (clamped to min)
compare.Clamp(150, 0, 100)   // 100 (clamped to max)

// Practical use: constrain percentage
pct := compare.Clamp(userInput, 0, 100)

Predicate Constructors

EqualOf

Create equality predicate.

isTwo := compare.EqualOf(2)
isTwo(2)    // true
isTwo(3)    // false

// Use with filter
twos := slicez.Filter(nums, compare.EqualOf(2))
IsZero

Check for zero value.

isZeroInt := compare.IsZero[int]()
isZeroInt(0)     // true
isZeroInt(5)     // false

emptyStrings := slicez.Filter(strings, compare.IsZero[string]())
IsNotZero

Check for non-zero value.

nonEmpty := slicez.Filter(strings, compare.IsNotZero[string]())
// Keeps only non-empty strings
NegateOf

Negate a predicate.

isEven := func(n int) bool { return n%2 == 0 }
isOdd := compare.NegateOf(isEven)
isOdd(3)     // true
isOdd(2)     // false

Function Utilities

Negate

Negate a comparison function.

// Create descending sort
nums := []int{1, 2, 3, 4, 5}
descending := slicez.SortBy(nums, compare.Negate(compare.Less[int]))
// descending = []int{5, 4, 3, 2, 1}
Identity

Identity function.

// Use when no transformation needed
result := slicez.Map(nums, compare.Identity[int])
Ternary

Conditional expression.

// If-else in one line
sign := compare.Ternary(x >= 0, "positive", "negative")
max := compare.Ternary(a > b, a, b)
Coalesce

First non-zero value.

// Provide fallback values
name := compare.Coalesce(userName, nickname, "Anonymous")
// Returns first non-empty string

port := compare.Coalesce(envPort, configPort, 8080)
// Returns first non-zero port

The Ordered Constraint

The Ordered constraint is defined in ordered.go:

type Ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
    ~float32 | ~float64 |
    ~string
}

Use it in your generic functions:

func Max[T compare.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

See Also

  • slicez - Use comparison functions with SortBy, Filter, etc.
  • setz - Set operations using comparisons

Documentation

Overview

Package compare provides comparison utilities and constraints for generic types.

The package defines the Ordered constraint for types that support ordering operations (integers, floats, strings) and provides comparison functions that work with any Ordered type.

Core comparison functions:

  • Compare: Three-way comparison returning -1, 0, or +1
  • Less/LessOrEqual/Greater/GreaterOrEqual: Standard comparisons
  • Equal: Equality comparison for comparable types

Predicate constructors (useful for filtering/matching):

  • EqualOf: Creates a predicate checking equality to a specific value
  • IsZero/IsNotZero: Check for zero values
  • NegateOf: Negates a predicate function

Utility functions:

  • Between: Check if a value is within a range (with inclusive/exclusive modes)
  • Clamp: Constrain a value to a range
  • Ternary: Conditional expression equivalent
  • Coalesce: Return first non-zero value from variadic arguments
  • Negate: Negate a comparison function
  • Identity: Identity function for comparable types

The Ordered constraint is defined in ordered.go and includes all ordered types (integers, unsigned integers, floats, and strings).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Asc added in v1.2.2

func Asc[E Ordered](a, b E) bool

Asc returns true if a < b. Shorthand for Less. Works with any Ordered type (integers, floats, strings). Useful for specifying ascending sort order with OrderBy.

Example:

nums := []int{5, 2, 8, 1, 9}
sorted := slicez.OrderBy(nums, compare.Identity[int], compare.Asc[int])
// sorted = []int{1, 2, 5, 8, 9}

func Between added in v1.2.0

func Between[E Ordered](value, start, end E, mode ...BetweenMode) bool

Between returns true if value is between start and end. By default, bounds are inclusive (start <= value <= end). Use BetweenMode options to control inclusivity.

Examples:

Between(5, 1, 10)                    // true (1 <= 5 <= 10)
Between(1, 1, 10)                    // true (bounds inclusive)
Between(1, 1, 10, BetweenExclusive)  // false (1 is not > 1)
Between(10, 1, 10, BetweenRightInclusive) // true (10 <= 10)

func Clamp added in v1.2.0

func Clamp[E Ordered](value, min, max E) E

Clamp constrains a value to be within the range [min, max]. If value is less than min, returns min. If value is greater than max, returns max. Otherwise returns the value unchanged.

Example:

Clamp(50, 0, 100)   // Returns 50 (within range)
Clamp(-10, 0, 100) // Returns 0 (clamped to min)
Clamp(150, 0, 100) // Returns 100 (clamped to max)
Clamp(5, 10, 20)   // Returns 10 (clamped to min)

func Coalesce added in v1.0.1

func Coalesce[T comparable](vals ...T) (t T)

Coalesce returns the first non-zero value from its arguments. Returns the zero value if all arguments are zero. Useful for providing fallback/default values.

Example:

// Use first non-empty string
name := compare.Coalesce(userName, nickName, "Anonymous")
// Returns userName if set, otherwise nickName, otherwise "Anonymous"

// Use first non-zero number
port := compare.Coalesce(configPort, envPort, 8080)
// Returns first non-zero port number, or 8080 as default

func Compare

func Compare[N Ordered](a N, b N) int

Compare performs a three-way comparison between two Ordered values. Returns -1 if a < b, 0 if a == b, and +1 if a > b.

Handles NaN values correctly for floats: NaN is considered less than any non-NaN value, and two NaN values are considered equal to each other.

Example:

compare.Compare(5, 10)   // Returns -1
compare.Compare(10, 10)  // Returns 0
compare.Compare(10, 5)   // Returns +1

// With floats including NaN
compare.Compare(math.NaN(), 5.0)  // Returns -1 (NaN < everything)
compare.Compare(math.NaN(), math.NaN()) // Returns 0 (NaN == NaN for comparison)

func Desc added in v1.2.2

func Desc[E Ordered](a, b E) bool

Desc returns true if a > b (descending order). Negation of Asc. Works with any Ordered type (integers, floats, strings). Useful for specifying descending sort order with OrderBy.

Example:

nums := []int{5, 2, 8, 1, 9}
sorted := slicez.OrderBy(nums, compare.Identity[int], compare.Desc[int])
// sorted = []int{9, 8, 5, 2, 1}

func Equal

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

Equal returns true if a == b. Works with any comparable type. Often used as a predicate function.

Example:

slicez.Filter([]int{1, 2, 3, 2, 4}, func(n int) bool {
    return compare.Equal(n, 2)
})
// Returns []int{2, 2}

func EqualOf

func EqualOf[N comparable](needle N) func(b N) bool

EqualOf returns a predicate function that checks if its argument equals the needle value. Useful for creating equality predicates for filtering or searching.

Example:

isTwo := compare.EqualOf(2)
isTwo(2) // true
isTwo(3) // false

// Use with slicez functions
nums := []int{1, 2, 3, 2, 4, 2}
filtered := slicez.Filter(nums, compare.EqualOf(2))
// filtered = []int{2, 2, 2}

func Greater added in v1.2.0

func Greater[E Ordered](a, b E) bool

Greater returns true if a > b

func GreaterOrEqual added in v1.2.0

func GreaterOrEqual[E Ordered](a, b E) bool

GreaterOrEqual returns true if a >= b

func Identity

func Identity[N comparable](n N) N

Identity returns its argument unchanged. Useful as a default mapper function or when a function type is required but no transformation is needed.

Example:

nums := []int{1, 2, 3, 4, 5}
evens := slicez.Filter(nums, func(n int) bool { return n%2 == 0 })
identity := slicez.Map(evens, compare.Identity[int])
// identity = []int{2, 4}

func IsNotZero added in v1.0.1

func IsNotZero[N comparable]() func(b N) bool

IsNotZero returns a predicate function that checks if its argument does NOT equal the zero value. Complement of IsZero. Useful for filtering out empty/null values.

Example:

filterNonZero := compare.IsNotZero[int]()
filterNonZero(0) // false
filterNonZero(5) // true

strings := []string{"hello", "", "world", ""}
nonEmpty := slicez.Filter(strings, compare.IsNotZero[string]())
// nonEmpty = []string{"hello", "world"}

func IsZero added in v1.0.1

func IsZero[N comparable]() func(b N) bool

IsZero returns a predicate function that checks if its argument equals the zero value. The zero value is type-dependent (0 for numbers, "" for strings, nil for pointers, etc.).

Example:

filterZeros := compare.IsZero[int]()
filterZeros(0) // true
filterZeros(5) // false

strings := []string{"hello", "", "world", ""}
empty := slicez.Filter(strings, compare.IsZero[string]())
// empty = []string{"", ""}

func Less

func Less[E Ordered](a, b E) bool

Less returns true if a < b. Works with any Ordered type (integers, floats, strings).

Example:

sorted := slicez.SortBy([]int{3, 1, 4, 1, 5}, compare.Less[int])
// sorted = []int{1, 1, 3, 4, 5}

func LessOrEqual

func LessOrEqual[E Ordered](a, b E) bool

LessOrEqual returns true if a <= b. Works with any Ordered type (integers, floats, strings).

Example:

compare.LessOrEqual(5, 10)  // true
compare.LessOrEqual(5, 5)   // true
compare.LessOrEqual(5, 3)   // false

func Negate

func Negate[A any](f func(a, b A) bool) func(A, A) bool

Negate returns a function that negates the result of the given comparison function. Useful for reversing sort orders or inverting predicates.

Example:

// Create a "greater than" comparison from "less than"
greater := compare.Negate(compare.Less[int])
greater(10, 5) // true (equivalent to 10 > 5)

// Reverse sort order
nums := []int{1, 2, 3, 4, 5}
descending := slicez.SortBy(nums, compare.Negate(compare.Less[int]))
// descending = []int{5, 4, 3, 2, 1}

func NegateOf

func NegateOf[A any](f func(A) bool) func(A) bool

NegateOf returns a function that negates the result of the given predicate function. Complement of Negate but works with single-argument predicates instead of comparisons.

Example:

isEven := func(n int) bool { return n%2 == 0 }
isOdd := compare.NegateOf(isEven)
isOdd(3) // true
isOdd(2) // false

func Ternary

func Ternary[A any](boolean bool, ifTrue A, ifFalse A) A

Ternary returns ifTrue if boolean is true, otherwise returns ifFalse. Equivalent to the ternary operator (cond ? a : b) found in many languages. All arguments are evaluated (not short-circuited).

Example:

sign := compare.Ternary(x >= 0, "positive", "negative")
// Returns "positive" if x >= 0, otherwise "negative"

max := compare.Ternary(a > b, a, b)
// Returns the larger of a and b

Types

type BetweenMode added in v1.2.0

type BetweenMode int

BetweenMode specifies whether bounds are inclusive or exclusive for the Between function.

const (
	// BetweenInclusive includes both start and end bounds (start <= x <= end)
	BetweenInclusive BetweenMode = iota
	// BetweenExclusive excludes both start and end bounds (start < x < end)
	BetweenExclusive
	// BetweenLeftInclusive includes only start bound (start <= x < end)
	BetweenLeftInclusive
	// BetweenRightInclusive includes only end bound (start < x <= end)
	BetweenRightInclusive
)

type Complex

type Complex interface {
	~complex64 | ~complex128
}

Complex is a constraint that permits any complex numeric type. If future releases of Go add new predeclared complex numeric types, this constraint will be modified to include them.

type Float

type Float interface {
	~float32 | ~float64
}

Float is a constraint that permits any floating-point type. If future releases of Go add new predeclared floating-point types, this constraint will be modified to include them.

type Integer

type Integer interface {
	Signed | Unsigned
}

Integer is a constraint that permits any integer type. If future releases of Go add new predeclared integer types, this constraint will be modified to include them.

type Number

type Number interface {
	~byte | Integer | Float
}

type Ordered

type Ordered interface {
	Integer | Float | ~string
}

Ordered is a constraint that permits any ordered type: any type that supports the operators < <= >= >. If future releases of Go add new ordered types, this constraint will be modified to include them.

type Signed

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

Signed is a constraint that permits any signed integer type. If future releases of Go add new predeclared signed integer types, this constraint will be modified to include them.

type SignedNumbers

type SignedNumbers interface {
	Signed | Float
}

type Unsigned

type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

Unsigned is a constraint that permits any unsigned integer type. If future releases of Go add new predeclared unsigned integer types, this constraint will be modified to include them.

Jump to

Keyboard shortcuts

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