Equality — Composable Equality Checks
Checking whether two values are equal is one of the most common requirements in software development. We do it to deduplicate arrays of items, detect changes in a user profile before saving, or confirm if an asset is already in a local collection.
For primitive types like strings, numbers, or booleans, JavaScript’s built-in === operator works
perfectly. However, for structured objects, arrays, and dates, === fails to capture our intent:
Two objects holding identical fields are not equal in standard JavaScript because they occupy
different locations in memory. Similarly, two Date objects representing the exact same millisecond
will evaluate to false when compared with ===.
To solve this, we often resort to serializing objects with
JSON.stringify(a) === JSON.stringify(b). But serialization is slow, fragile (it fails if keys are
written in a different order), and structurally incapable of handling functions, dates, or custom
maps. Alternatively, we write one-off comparison functions by hand, but they do not compose — you
cannot combine two checkers into one, or lift a string checker to compare record fields without
writing new boilerplate from scratch.
Equality<A> solves this mismatch. It represents a first-class, pure description of equivalence:
Any binary function matching this signature is a valid Equality checker. By treating equality as a
composable value, we can construct, name, and combine equivalence checkers point-free.
Built-In Equivalence Instances
Section titled “Built-In Equivalence Instances”The library provides optimized, built-in instances for common primitive types:
Deep Array Equivalence
Section titled “Deep Array Equivalence”To compare arrays, we pass the element-level equivalence checker to Equality.array:
Comparing by Fields: by
Section titled “Comparing by Fields: by”Equality.by adapts an equality checker designed for type A to operate on a richer type B by
extracting the target field to compare:
Combining Checks: and
Section titled “Combining Checks: and”When we need to assert that two complex objects are equivalent across multiple different fields, we
combine their respective checkers using and:
Practical Application: Custom Deduplication
Section titled “Practical Application: Custom Deduplication”Arr.uniqWith accepts any Equality<A> instance to remove duplicate values from a collection,
allowing you to deduplicate complex objects structurally:
When to use Equality
Section titled “When to use Equality”Use Equality when:
Section titled “Use Equality when:”- Comparing complex data structures: You are diffing object records, deeply comparing nested arrays, or validating calendar dates.
- Performing custom deduplication: You need to filter unique objects based on structurally
matched fields using helpers like
Arr.uniqWith. - Composing checks: You want to name small, individual field checkers and build exact matchers
cleanly using
and.
Keep using === directly when:
Section titled “Keep using === directly when:”- Comparing primitives: You are comparing plain
string,number, orbooleanvariables within a narrow, non-pipelined scope.