receta

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.

Signature
function ascending<T, C extends number | string | Date>(fn: ComparableExtractor<T, C>): Comparator<T>
Example
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.

Signature
function byBoolean<T>(fn: ComparableExtractor<T, boolean>): Comparator<T>
Example
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.

Signature
function byDate<T>(fn: ComparableExtractor<T, Date>): Comparator<T>
Example
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.

Signature
function byKey<T, K extends keyof T>(key: K): Comparator<T>
Example
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.

Signature
function byNumber<T>(fn: ComparableExtractor<T, number>): Comparator<T>
Example
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.

Signature
function byString<T>(fn: ComparableExtractor<T, string>, options?: StringCompareOptions): Comparator<T>
Example
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.

Signature
function caseInsensitive<T>(fn: ComparableExtractor<T, string>): Comparator<T>
Example
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.md

See 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".

Signature
function compose<T>(...comparators: Comparator<T>[]): Comparator<T>
Example
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.

Signature
function descending<T, C extends number | string | Date>(fn: ComparableExtractor<T, C>): Comparator<T>
Example
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.

Signature
function localeCompare<T>(fn: ComparableExtractor<T, string>, locale: string): Comparator<T>
Example
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
Signature
function natural<T>(fn: ComparableExtractor<T, string>): Comparator<T>
Example
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.

Signature
function nullsFirst<T>(comparator: Comparator<Nullable<T>>): Comparator<Nullable<T>>
Example
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.

Signature
function nullsLast<T>(comparator: Comparator<Nullable<T>>): Comparator<Nullable<T>>
Example
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.

Signature
function reverse<T>(comparator: Comparator<T>): Comparator<T>
Example
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.

Signature
function withTiebreaker<T>(primary: Comparator<T>, tiebreaker: Comparator<T>): Comparator<T>
Example
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.

Signature
type ComparableExtractor<T, C = any> = (value: T) => C

Comparator

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
Signature
type Comparator<T> = (a: T, b: T) => number
Example
const 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.

Signature
type Nullable<T> = T | null | undefined

StringCompareOptions

Options for string comparison.

Signature
interface StringCompareOptions {
  readonly caseSensitive?: boolean

  readonly locale?: string
}

On this page