Reference · Collections
Record Equality
Comparing maps for equality. Use the Eq type class to define custom equality semantics for map values.
Core API
| Function | Signature | Description |
|---|---|---|
Eq | func Eq[K comparable, V any](Eq[V]) Eq[map[K]V] | Create map equality |
Usage Examples
Basic Equality
basic.go
import (
R "github.com/IBM/fp-go/v2/record"
E "github.com/IBM/fp-go/v2/eq"
N "github.com/IBM/fp-go/v2/number"
)
// Create record equality from value equality
recordEq := R.Eq(N.Eq)
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1, "b": 2}
m3 := map[string]int{"a": 1, "b": 3}
m4 := map[string]int{"a": 1} // Different size
recordEq.Equals(m1, m2) // true - same keys and values
recordEq.Equals(m1, m3) // false - different values
recordEq.Equals(m1, m4) // false - different keys
String Equality
string.go
import S "github.com/IBM/fp-go/v2/string"
stringRecordEq := R.Eq(S.Eq)
m1 := map[string]string{"name": "Alice", "role": "admin"}
m2 := map[string]string{"name": "Alice", "role": "admin"}
m3 := map[string]string{"name": "alice", "role": "admin"}
stringRecordEq.Equals(m1, m2) // true
stringRecordEq.Equals(m1, m3) // false - case sensitive
Custom Struct Equality
struct.go
type User struct {
ID int
Name string
Age int
}
// Compare by ID only
userEq := E.FromEquals(func(a, b User) bool {
return a.ID == b.ID
})
recordUserEq := R.Eq(userEq)
m1 := map[string]User{
"alice": {ID: 1, Name: "Alice", Age: 30},
}
m2 := map[string]User{
"alice": {ID: 1, Name: "Alice Updated", Age: 31},
}
recordUserEq.Equals(m1, m2) // true - same ID
Case-Insensitive Equality
case_insensitive.go
// Case-insensitive string equality
caseInsensitiveEq := E.FromEquals(func(a, b string) bool {
return strings.ToLower(a) == strings.ToLower(b)
})
recordEq := R.Eq(caseInsensitiveEq)
m1 := map[string]string{"name": "Alice"}
m2 := map[string]string{"name": "ALICE"}
recordEq.Equals(m1, m2) // true
Nested Map Equality
nested.go
// Equality for nested maps
innerEq := R.Eq(N.Eq)
outerEq := R.Eq(innerEq)
m1 := map[string]map[string]int{
"group1": {"a": 1, "b": 2},
"group2": {"c": 3},
}
m2 := map[string]map[string]int{
"group1": {"a": 1, "b": 2},
"group2": {"c": 3},
}
outerEq.Equals(m1, m2) // true
Array Value Equality
array.go
import A "github.com/IBM/fp-go/v2/array"
// Equality for maps with array values
arrayEq := A.Eq(N.Eq)
recordArrayEq := R.Eq(arrayEq)
m1 := map[string][]int{
"nums1": {1, 2, 3},
"nums2": {4, 5},
}
m2 := map[string][]int{
"nums1": {1, 2, 3},
"nums2": {4, 5},
}
recordArrayEq.Equals(m1, m2) // true
Approximate Float Equality
float.go
// Approximate equality for floats
const epsilon = 0.0001
floatEq := E.FromEquals(func(a, b float64) bool {
return math.Abs(a-b) < epsilon
})
recordFloatEq := R.Eq(floatEq)
m1 := map[string]float64{"pi": 3.14159}
m2 := map[string]float64{"pi": 3.14160}
recordFloatEq.Equals(m1, m2) // true - within epsilon
Common Patterns
Configuration Comparison
config.go
type Config struct {
Host string
Port int
Timeout int
}
configEq := E.FromEquals(func(a, b Config) bool {
return a.Host == b.Host && a.Port == b.Port
// Ignore Timeout in comparison
})
recordConfigEq := R.Eq(configEq)
configs1 := map[string]Config{
"prod": {Host: "api.example.com", Port: 443, Timeout: 30},
}
configs2 := map[string]Config{
"prod": {Host: "api.example.com", Port: 443, Timeout: 60},
}
recordConfigEq.Equals(configs1, configs2) // true - timeout ignored
Testing Helper
testing.go
func AssertMapsEqual[K comparable, V any](
t *testing.T,
eq E.Eq[V],
expected, actual map[K]V,
) {
recordEq := R.Eq(eq)
if !recordEq.Equals(expected, actual) {
t.Errorf("Maps not equal:
Expected: %v
Actual: %v",
expected, actual)
}
}
// Usage in tests
func TestSomething(t *testing.T) {
expected := map[string]int{"a": 1, "b": 2}
actual := processData()
AssertMapsEqual(t, N.Eq, expected, actual)
}
Semantic Equality
semantic.go
type Status string
const (
Active Status = "active"
Inactive Status = "inactive"
Enabled Status = "enabled"
Disabled Status = "disabled"
)
// Treat active/enabled and inactive/disabled as equal
statusEq := E.FromEquals(func(a, b Status) bool {
normalize := func(s Status) string {
if s == Active || s == Enabled {
return "active"
}
return "inactive"
}
return normalize(a) == normalize(b)
})
recordStatusEq := R.Eq(statusEq)
m1 := map[string]Status{"service1": Active}
m2 := map[string]Status{"service1": Enabled}
recordStatusEq.Equals(m1, m2) // true - semantically equal
Key Equality: Map keys must be comparable types in Go. The Eq instance only applies to values, not keys.
Custom Equality: Define custom Eq instances to implement domain-specific equality semantics, such as case-insensitive comparison or approximate numeric equality.