Compare
19 exports from @ontologyio/receta/compare.
Everything exported from @ontologyio/receta/compare. New here? Start with the Compare guide.
Functions
ascending
Creates a comparator that sorts in ascending order based on an extracted value.
Works with numbers, strings, dates, and any comparable values.
function ascending<T, C extends number | string | Date>(fn: ComparableExtractor<T, C>): Comparator<T>interface User {
name: string
age: number
}
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 }
]
// Sort by age ascending
users.sort(ascending(u => u.age))
// => [{ name: 'Bob', age: 25 }, { name: 'Alice', age: 30 }, { name: 'Charlie', age: 35 }]
// Sort by name ascending
users.sort(ascending(u => u.name))
// => [{ name: 'Alice', ... }, { name: 'Bob', ... }, { name: 'Charlie', ... }]See also descending, byKey.
byBoolean
Creates a comparator for boolean values.
By default, false comes before true (false < true).
Use reverse() for true-first sorting.
function byBoolean<T>(fn: ComparableExtractor<T, boolean>): Comparator<T>interface Task {
title: string
completed: boolean
urgent: boolean
}
const tasks = [
{ title: 'Review PR', completed: true, urgent: false },
{ title: 'Fix bug', completed: false, urgent: true },
{ title: 'Write docs', completed: false, urgent: false },
{ title: 'Deploy', completed: true, urgent: true }
]
// Sort by completed (incomplete first)
tasks.sort(byBoolean(t => t.completed))
// => [Fix bug (false), Write docs (false), Review PR (true), Deploy (true)]
// Sort by urgent (urgent first)
tasks.sort(reverse(byBoolean(t => t.urgent)))
// => [Fix bug (true), Deploy (true), Review PR (false), Write docs (false)]
// Multi-level: urgent first, then incomplete first
tasks.sort(
compose(
reverse(byBoolean(t => t.urgent)),
byBoolean(t => t.completed)
)
)See also reverse.
byDate
Creates a comparator for date values.
Sorts dates chronologically (earliest to latest by default). Handles Date objects correctly for temporal sorting.
function byDate<T>(fn: ComparableExtractor<T, Date>): Comparator<T>interface Event {
name: string
occurredAt: Date
scheduledFor: Date
}
const events = [
{ name: 'Meeting', occurredAt: new Date('2024-01-15'), scheduledFor: new Date('2024-02-01') },
{ name: 'Launch', occurredAt: new Date('2024-01-10'), scheduledFor: new Date('2024-03-01') },
{ name: 'Review', occurredAt: new Date('2024-01-20'), scheduledFor: new Date('2024-02-15') }
]
// Sort by occurred date (earliest first)
events.sort(byDate(e => e.occurredAt))
// => [Launch (Jan 10), Meeting (Jan 15), Review (Jan 20)]
// Sort by scheduled date descending (latest first)
events.sort(reverse(byDate(e => e.scheduledFor)))
// => [Launch (Mar 1), Review (Feb 15), Meeting (Feb 1)]See also ascending, descending.
byKey
Creates a comparator that sorts by a specific object key.
Convenience function for the common case of sorting objects by a property.
function byKey<T, K extends keyof T>(key: K): Comparator<T>interface Product {
id: string
name: string
price: number
stock: number
}
const products = [
{ id: 'c', name: 'Keyboard', price: 80, stock: 5 },
{ id: 'a', name: 'Mouse', price: 25, stock: 10 },
{ id: 'b', name: 'Monitor', price: 300, stock: 3 }
]
// Sort by price
products.sort(byKey('price'))
// => [{ name: 'Mouse', price: 25, ... }, { name: 'Keyboard', price: 80, ... }, ...]
// Sort by name
products.sort(byKey('name'))
// => [{ name: 'Keyboard', ... }, { name: 'Monitor', ... }, { name: 'Mouse', ... }]
// Sort by ID
products.sort(byKey('id'))
// => [{ id: 'a', ... }, { id: 'b', ... }, { id: 'c', ... }]See also ascending, descending.
byNumber
Creates a comparator for numeric values.
Handles all numeric types including integers, floats, and special values (NaN, Infinity). NaN values are treated as equal and sorted to the end.
function byNumber<T>(fn: ComparableExtractor<T, number>): Comparator<T>interface Metric {
name: string
value: number
change: number
}
const metrics = [
{ name: 'CPU', value: 45.2, change: 1.5 },
{ name: 'Memory', value: 78.9, change: -2.3 },
{ name: 'Disk', value: 23.1, change: 0.8 }
]
// Sort by value (lowest first)
metrics.sort(byNumber(m => m.value))
// => [Disk (23.1), CPU (45.2), Memory (78.9)]
// Sort by change (highest first)
metrics.sort(reverse(byNumber(m => m.change)))
// => [CPU (+1.5), Disk (+0.8), Memory (-2.3)]See also ascending, descending.
byString
Creates a comparator for string values with optional configuration.
Supports case-sensitive/insensitive comparison and locale-aware sorting.
function byString<T>(fn: ComparableExtractor<T, string>, options?: StringCompareOptions): Comparator<T>interface User {
firstName: string
lastName: string
email: string
}
const users = [
{ firstName: 'alice', lastName: 'Smith', email: 'alice@example.com' },
{ firstName: 'Bob', lastName: 'Jones', email: 'bob@example.com' },
{ firstName: 'Charlie', lastName: 'Adams', email: 'charlie@example.com' }
]
// Case-sensitive sort (default)
users.sort(byString(u => u.firstName))
// => [Bob, Charlie, alice] (uppercase comes first)
// Case-insensitive sort
users.sort(byString(u => u.firstName, { caseSensitive: false }))
// => [alice, Bob, Charlie]
// Locale-aware sort
const germanNames = [
{ name: 'Müller' },
{ name: 'Mueller' },
{ name: 'Möller' }
]
germanNames.sort(byString(n => n.name, { locale: 'de-DE' }))See also caseInsensitive, localeCompare.
caseInsensitive
Creates a case-insensitive string comparator.
Convenience wrapper for byString with caseSensitive: false.
function caseInsensitive<T>(fn: ComparableExtractor<T, string>): Comparator<T>interface File {
name: string
extension: string
}
const files = [
{ name: 'README.md', extension: 'md' },
{ name: 'index.ts', extension: 'ts' },
{ name: 'App.tsx', extension: 'tsx' },
{ name: 'package.json', extension: 'json' }
]
// Case-sensitive (default) - uppercase comes first
files.sort(byString(f => f.name))
// Order: App.tsx, README.md, index.ts, package.json
// Case-insensitive - alphabetical regardless of case
files.sort(caseInsensitive(f => f.name))
// Order: App.tsx, index.ts, package.json, README.mdSee also byString, natural.
compose
Composes multiple comparators into a single comparator with priority ordering.
Applies comparators in order: if the first comparator returns 0 (equal), tries the second, and so on. This enables multi-level sorting like "sort by status, then by priority, then by date".
function compose<T>(...comparators: Comparator<T>[]): Comparator<T>interface Issue {
status: 'open' | 'closed'
priority: 'low' | 'medium' | 'high'
createdAt: Date
}
const issues: Issue[] = [
{ status: 'open', priority: 'high', createdAt: new Date('2024-01-03') },
{ status: 'open', priority: 'low', createdAt: new Date('2024-01-01') },
{ status: 'closed', priority: 'high', createdAt: new Date('2024-01-02') },
{ status: 'open', priority: 'high', createdAt: new Date('2024-01-04') }
]
// Sort by: status (open first), then priority (high first), then date (oldest first)
const priorityOrder = { high: 0, medium: 1, low: 2 }
const statusOrder = { open: 0, closed: 1 }
issues.sort(
compose(
ascending(i => statusOrder[i.status]),
ascending(i => priorityOrder[i.priority]),
ascending(i => i.createdAt)
)
)See also withTiebreaker.
descending
Creates a comparator that sorts in descending order based on an extracted value.
Works with numbers, strings, dates, and any comparable values.
function descending<T, C extends number | string | Date>(fn: ComparableExtractor<T, C>): Comparator<T>interface Transaction {
amount: number
date: Date
}
const transactions = [
{ amount: 100, date: new Date('2024-01-01') },
{ amount: 500, date: new Date('2024-01-03') },
{ amount: 200, date: new Date('2024-01-02') }
]
// Sort by amount descending (highest first)
transactions.sort(descending(t => t.amount))
// => [{ amount: 500, ... }, { amount: 200, ... }, { amount: 100, ... }]
// Sort by date descending (most recent first)
transactions.sort(descending(t => t.date))
// => [2024-01-03, 2024-01-02, 2024-01-01]See also ascending, byKey.
localeCompare
Creates a locale-aware string comparator.
Uses the browser/Node.js locale system for culturally appropriate sorting. Handles accented characters, special characters, and language-specific rules.
function localeCompare<T>(fn: ComparableExtractor<T, string>, locale: string): Comparator<T>interface City {
name: string
country: string
}
const cities = [
{ name: 'Zürich', country: 'Switzerland' },
{ name: 'Berlin', country: 'Germany' },
{ name: 'München', country: 'Germany' },
{ name: 'Åarhus', country: 'Denmark' }
]
// German locale (handles umlauts correctly)
cities.sort(localeCompare(c => c.name, 'de-DE'))
// => [Åarhus, Berlin, München, Zürich]
// French locale
const names = [
{ name: 'Étienne' },
{ name: 'Eric' },
{ name: 'Émile' }
]
names.sort(localeCompare(n => n.name, 'fr-FR'))
// => [Émile, Eric, Étienne]
// English locale
const words = [
{ word: 'résumé' },
{ word: 'resume' },
{ word: 'result' }
]
words.sort(localeCompare(w => w.word, 'en-US'))See also byString, caseInsensitive.
natural
Creates a comparator for natural string sorting.
Natural sorting handles numbers within strings intelligently:
- "file2.txt" comes before "file10.txt" (not after as with lexicographic sort)
- Properly handles mixed alphanumeric strings
function natural<T>(fn: ComparableExtractor<T, string>): Comparator<T>const files = ['file10.txt', 'file2.txt', 'file1.txt', 'file20.txt']
// Lexicographic sort (incorrect for numbers)
files.sort() // => ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt']
// Natural sort (correct for numbers)
files.sort(natural(x => x))
// => ['file1.txt', 'file2.txt', 'file10.txt', 'file20.txt']
// With objects
interface File {
name: string
size: number
}
const fileObjects = [
{ name: 'image10.png', size: 1024 },
{ name: 'image2.png', size: 2048 },
{ name: 'image1.png', size: 512 }
]
fileObjects.sort(natural(f => f.name))
// => [{ name: 'image1.png', ... }, { name: 'image2.png', ... }, { name: 'image10.png', ... }]See also ascending, caseInsensitive.
nullsFirst
Wraps a comparator to place null items first.
It checks the items being compared, not a field inside them. To order objects
by a nullable field, give the extractor a fallback that sorts where the nulls
should land: ascending(u => u.lastLogin ?? new Date(0)).
When comparing two null/undefined values, they are considered equal.
When comparing null/undefined with a non-null value, null comes first.
Array.prototype.sort moves undefined to the end without calling the
comparator, so in practice this reorders null.
function nullsFirst<T>(comparator: Comparator<Nullable<T>>): Comparator<Nullable<T>>const scores: (number | null)[] = [30, null, 10, null, 20]
// The extractor's fallback satisfies the types. A null item never reaches it.
scores.sort(nullsFirst(ascending(score => score ?? 0)))
// => [null, null, 10, 20, 30]See also nullsLast.
nullsLast
Wraps a comparator to place null items last.
It checks the items being compared, not a field inside them. To order objects
by a nullable field, give the extractor a fallback that sorts where the nulls
should land: descending(p => p.discount ?? 0).
When comparing two null/undefined values, they are considered equal. When comparing null/undefined with a non-null value, null comes last.
function nullsLast<T>(comparator: Comparator<Nullable<T>>): Comparator<Nullable<T>>const scores: (number | null)[] = [30, null, 10, null, 20]
// The extractor's fallback satisfies the types. A null item never reaches it.
scores.sort(nullsLast(ascending(score => score ?? 0)))
// => [10, 20, 30, null, null]See also nullsFirst.
reverse
Reverses the order of a comparator.
Useful for inverting existing comparators without rewriting logic.
function reverse<T>(comparator: Comparator<T>): Comparator<T>const users = [
{ name: 'Alice', score: 100 },
{ name: 'Bob', score: 200 },
{ name: 'Charlie', score: 150 }
]
// Sort by score ascending
const scoreAsc = ascending((u: { score: number }) => u.score)
users.sort(scoreAsc) // Scores: 100, 150, 200
// Reverse it for descending
users.sort(reverse(scoreAsc)) // Scores: 200, 150, 100
// Equivalent to:
users.sort(descending(u => u.score))See also ascending, descending.
withTiebreaker
Combines a primary comparator with a tiebreaker for when values are equal.
More explicit alternative to compose for two-level sorting.
function withTiebreaker<T>(primary: Comparator<T>, tiebreaker: Comparator<T>): Comparator<T>interface Player {
name: string
score: number
level: number
}
const players = [
{ name: 'Alice', score: 1000, level: 5 },
{ name: 'Bob', score: 1000, level: 3 },
{ name: 'Charlie', score: 800, level: 4 }
]
// Sort by score (descending), break ties by level (descending)
players.sort(
withTiebreaker(
descending(p => p.score),
descending(p => p.level)
)
)
// => [{ name: 'Alice', score: 1000, level: 5 },
// { name: 'Bob', score: 1000, level: 3 },
// { name: 'Charlie', score: 800, level: 4 }]See also compose.
Types
ComparableExtractor
Helper type for extracting a comparable value from T.
type ComparableExtractor<T, C = any> = (value: T) => CComparator
A comparator function for sorting values of type T.
Returns:
- Negative number if a should come before b
- Zero if a and b are equal
- Positive number if a should come after b
type Comparator<T> = (a: T, b: T) => numberconst numericComparator: Comparator<number> = (a, b) => a - b
const numbers = [3, 1, 2]
numbers.sort(numericComparator) // => [1, 2, 3]Nullable
Represents a value that may be null or undefined.
type Nullable<T> = T | null | undefinedStringCompareOptions
Options for string comparison.
interface StringCompareOptions {
readonly caseSensitive?: boolean
readonly locale?: string
}