receta

Number

34 exports from @ontologyio/receta/number.

Everything exported from @ontologyio/receta/number.

Functions

average

Calculates the average (mean) of an array of numbers.

Returns None for an empty array to avoid division by zero.

Signature
function average(values: readonly number[]): Option<number>
Example
average([1, 2, 3, 4]) // => Some(2.5)
average([10]) // => Some(10)
average([]) // => None

// With Option handling
pipe(
  average(scores),
  unwrapOr(0)
)

// Real-world: Rating calculation
const calculateRating = (ratings: number[]) =>
  pipe(
    average(ratings),
    map(rating => rating.toFixed(1))
  )

See also sum.

clamp

Constrains a number to be within a specified range.

If the value is less than min, returns min. If the value is greater than max, returns max. Otherwise, returns the value unchanged.

Data-first
function clamp(value: number, min: number, max: number): number
Data-last
function clamp(min: number, max: number): (value: number) => number
Example
// Data-first
clamp(50, 0, 100) // => 50
clamp(150, 0, 100) // => 100
clamp(-10, 0, 100) // => 0

// Data-last (in pipe)
pipe(
  userInput,
  clamp(0, 100)
) // => constrained to 0-100

// Real-world: Volume control
const setVolume = (level: number) =>
  pipe(level, clamp(0, 100))

See also inRange.

format

Formats a number with specified decimal places and grouping.

Uses the Intl.NumberFormat API for locale-aware formatting.

Data-first
function format(value: number, options?: FormatOptions | undefined): string
Data-last
function format(options?: FormatOptions | undefined): (value: number) => string
Example
// Data-first
format(1234.5678) // => "1,234.57"
format(1234.5678, { decimals: 0 }) // => "1,235"
format(1234.5678, { useGrouping: false }) // => "1234.57"
format(1234.5678, { locale: 'de-DE' }) // => "1.234,57"

// Data-last (in pipe)
pipe(
  price,
  format({ decimals: 2 })
) // => formatted price

See also toCurrency, toPercent.

fromBytes

Parses a byte size string into a number of bytes.

Handles common formats: "1KB", "1.5 MB", "2GB". Supports both binary (1024) and decimal (1000) bases. Case-insensitive unit parsing.

Signature
function fromBytes(input: string, base?: "binary" | "decimal"): Result<number, ParseError>

Fails with ParseError.

Example
fromBytes("1KB") // => Ok(1024)
fromBytes("1.5 MB") // => Ok(1572864)
fromBytes("2GB") // => Ok(2147483648)
fromBytes("100 B") // => Ok(100)
fromBytes("1KB", 'decimal') // => Ok(1000)
fromBytes("invalid") // => Err({ code: 'PARSE_ERROR', ... })

// Real-world: Parse file size limit
const validateFileSize = (sizeStr: string, maxStr: string) =>
  pipe(
    collect([fromBytes(sizeStr), fromBytes(maxStr)]),
    map(([size, max]) => size <= max)
  )

See also toBytes.

fromCurrency

Parses a currency string into a number.

Strips currency symbols, grouping separators, and whitespace before parsing. Handles common formats: "$1,234.56", "€1.234,56", "£1,234.56".

Signature
function fromCurrency(input: string): Result<number, ParseError>

Fails with ParseError.

Example
fromCurrency("$1,234.56") // => Ok(1234.56)
fromCurrency("€1.234,56") // => Ok(1234.56) (European format)
fromCurrency("£1,234") // => Ok(1234)
fromCurrency("-$50.00") // => Ok(-50)
fromCurrency("invalid") // => Err({ code: 'PARSE_ERROR', ... })

// Real-world: Parse Stripe amount string
const parseStripeAmount = (amountStr: string) =>
  pipe(
    fromCurrency(amountStr),
    map(amount => Math.round(amount * 100)) // Convert to cents
  )

See also fromString, toCurrency.

fromString

Parses a string into a number.

Returns Ok with the parsed number, or Err with ParseError if parsing fails. Handles common number formats including decimals, negatives, and scientific notation.

Signature
function fromString(input: string): Result<number, ParseError>

Fails with ParseError.

Example
fromString("123") // => Ok(123)
fromString("123.45") // => Ok(123.45)
fromString("-42") // => Ok(-42)
fromString("1.23e4") // => Ok(12300)
fromString("abc") // => Err({ code: 'PARSE_ERROR', ... })
fromString("") // => Err({ code: 'PARSE_ERROR', ... })

// Real-world: Form input validation
const validatePrice = (input: string) =>
  pipe(
    fromString(input),
    flatMap(price =>
      price > 0 ? ok(price) : err({ code: 'INVALID_PRICE' })
    )
  )

See also fromCurrency.

inRange

Checks if a number is within a specified range (inclusive).

Returns true if the value is between min and max (inclusive). Can be used as a predicate builder for filtering.

Data-first
function inRange(value: number, min: number, max: number): boolean
Data-last
function inRange(min: number, max: number): (value: number) => boolean
Example
// Data-first
inRange(50, 0, 100) // => true
inRange(150, 0, 100) // => false
inRange(0, 0, 100) // => true
inRange(100, 0, 100) // => true

// Data-last (as predicate)
const ages = [15, 25, 35, 45]
R.filter(ages, inRange(18, 65)) // => [25, 35, 45]

See also clamp.

interpolate

Linearly interpolates between two numbers.

Given a progress value between 0 and 1, returns the interpolated value between from and to. Useful for animations, transitions, and gradients.

Data-first
function interpolate(from: number, to: number, progress: number): number
Data-last
function interpolate(from: number, to: number): (progress: number) => number
Example
// Data-first
interpolate(0, 100, 0.5) // => 50
interpolate(0, 100, 0) // => 0
interpolate(0, 100, 1) // => 100
interpolate(10, 20, 0.25) // => 12.5

// Data-last (in pipe)
pipe(
  progress,
  interpolate(startValue, endValue)
)

// Real-world: Animation easing
const animateValue = (start: number, end: number, duration: number) => {
  const startTime = Date.now()
  return () => {
    const elapsed = Date.now() - startTime
    const progress = Math.min(elapsed / duration, 1)
    return interpolate(start, end, progress)
  }
}

isFinite

Type guard to check if a value is a finite number.

Returns true if the value is a finite number (not Infinity, -Infinity, or NaN).

Signature
function isFinite(value: number): boolean
Example
isFinite(42) // => true
isFinite(42.5) // => true
isFinite(Infinity) // => false
isFinite(-Infinity) // => false
isFinite(NaN) // => false

See also isInteger.

isInteger

Type guard to check if a value is a safe integer.

Returns true if the value is an integer within JavaScript's safe integer range (Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER).

Signature
function isInteger(value: number): boolean
Example
isInteger(42) // => true
isInteger(42.5) // => false
isInteger(Number.MAX_SAFE_INTEGER) // => true
isInteger(Number.MAX_SAFE_INTEGER + 1) // => false

See also isFinite, inRange.

isNegative

Type guard to check if a number is negative.

Returns true if the value is less than zero.

Signature
function isNegative(value: number): boolean
Example
isNegative(-5) // => true
isNegative(0) // => false
isNegative(42) // => false

See also isPositive.

isPositive

Type guard to check if a number is positive.

Returns true if the value is greater than zero.

Signature
function isPositive(value: number): boolean
Example
import { isPositive } from '@ontologyio/receta/number'

isPositive(42) // => true
isPositive(0) // => false
isPositive(-5) // => false

See also isNegative.

normalize

Scales a number from a given range to the 0-1 range.

Useful for data visualization, progress bars, animations, and machine learning feature scaling.

Data-first
function normalize(value: number, min: number, max: number): number
Data-last
function normalize(min: number, max: number): (value: number) => number
Example
// Data-first
normalize(75, 0, 100) // => 0.75
normalize(50, 0, 100) // => 0.5
normalize(5, 0, 10) // => 0.5
normalize(0, 0, 100) // => 0
normalize(100, 0, 100) // => 1

// Data-last (in pipe)
pipe(
  currentValue,
  normalize(minValue, maxValue)
)

// Real-world: Progress bar
const progressPercentage = (current: number, total: number) =>
  pipe(current, normalize(0, total), (n) => n * 100)

// Real-world: Feature scaling for ML
const scaleFeature = (values: number[]) => {
  const min = Math.min(...values)
  const max = Math.max(...values)
  return R.map(values, normalize(min, max))
}

See also interpolate.

parseFormattedNumber

Parses a formatted number string (with thousands separators and currency symbols) into a number.

Handles:

  • Thousands separators (commas, spaces, periods)
  • Currency symbols ($ , £, €, etc.)
  • Parentheses for negative numbers (accounting format)
  • Decimal points
  • Negative signs

Returns Ok with the parsed number, or Err with ParseError if parsing fails.

Signature
function parseFormattedNumber(input: string): Result<number, ParseError>

Fails with ParseError.

Example
// Basic formatted numbers
parseFormattedNumber("1,234.56") // => Ok(1234.56)
parseFormattedNumber("1 234.56") // => Ok(1234.56)
parseFormattedNumber("1.234,56") // => Ok(1234.56) - European format

// Currency symbols
parseFormattedNumber("$1,234.56") // => Ok(1234.56)
parseFormattedNumber("£1,234.56") // => Ok(1234.56)
parseFormattedNumber("€1.234,56") // => Ok(1234.56)
parseFormattedNumber("1,234.56 USD") // => Ok(1234.56)

// Negative numbers
parseFormattedNumber("-1,234.56") // => Ok(-1234.56)
parseFormattedNumber("(1,234.56)") // => Ok(-1234.56) - Accounting format

// Invalid input
parseFormattedNumber("abc") // => Err({ code: 'PARSE_ERROR', ... })
parseFormattedNumber("") // => Err({ code: 'PARSE_ERROR', ... })

// Real-world: Parse user input
const validatePrice = (input: string) =>
  pipe(
    parseFormattedNumber(input),
    flatMap(price =>
      price > 0 ? ok(price) : err({ code: 'INVALID_PRICE' })
    )
  )

// Real-world: Parse financial report
const parseRevenue = (text: string) =>
  parseFormattedNumber(text) // "$1,234,567.89" => 1234567.89

See also fromString, fromCurrency.

percentage

Calculates what percentage one number is of another.

Returns the percentage as a decimal (0.25 = 25%). Returns 0 if the total is 0 to avoid division by zero.

Data-first
function percentage(value: number, total: number): number
Data-last
function percentage(total: number): (value: number) => number
Example
// Data-first
percentage(25, 100) // => 0.25
percentage(1, 4) // => 0.25
percentage(10, 0) // => 0 (safe division)

// Data-last (in pipe)
pipe(
  completed,
  percentage(total),
  toPercent(1)
) // => "25.0%"

// Real-world: Progress indicator
const progress = (current: number, total: number) =>
  pipe(
    percentage(current, total),
    (p) => p * 100,
    Math.round
  )

See also toPercent.

random

Generates a random number within a specified range.

Returns a random number between min (inclusive) and max (exclusive). If only one argument is provided, it's treated as max with min defaulting to 0.

Overload 1
function random(max: number): number
Overload 2
function random(min: number, max: number): number
Example
random(10) // => number between 0 and 10
random(5, 10) // => number between 5 and 10
random(0, 1) // => number between 0 and 1

// Real-world: Random delay for retry backoff
const jitter = () => random(0, 1000)
const backoffWithJitter = (attempt: number) =>
  Math.pow(2, attempt) * 1000 + jitter()

See also step.

ratio

Calculates the ratio of two numbers.

Returns the ratio as a decimal (a / b). Returns 0 if b is 0 to avoid division by zero.

Data-first
function ratio(a: number, b: number): number
Data-last
function ratio(b: number): (a: number) => number
Example
// Data-first
ratio(4, 2) // => 2
ratio(3, 4) // => 0.75
ratio(10, 0) // => 0 (safe division)

// Data-last (in pipe)
pipe(
  width,
  ratio(height)
) // => aspect ratio

// Real-world: Aspect ratio
const aspectRatio = (width: number, height: number) =>
  pipe(
    ratio(width, height),
    round(2)
  )

round

Rounds a number to a specified number of decimal places.

Uses proper rounding (half away from zero).

Data-first
function round(value: number, decimals?: number | undefined): number
Data-last
function round(decimals?: number | undefined): (value: number) => number
Example
// Data-first (requires 2 args)
round(1.2345, 0) // => 1
round(1.2345, 2) // => 1.23
round(1.2367, 2) // => 1.24
round(1234.56, -2) // => 1200

// Data-last (in pipe)
pipe(
  price,
  round(2)
)

// Real-world: Price rounding
const roundPrice = (price: number) =>
  pipe(price, round(2))

See also toPrecision.

roundTo

Rounds a number to the nearest multiple of a step value.

Useful for quantizing values to specific increments like price steps, slider values, or decimal quantization.

Data-first
function roundTo(value: number, step: number): number
Data-last
function roundTo(step: number): (value: number) => number
Example
// Data-first
roundTo(4.23, 0.25) // => 4.25
roundTo(4.22, 0.25) // => 4.25
roundTo(4.10, 0.25) // => 4.00
roundTo(127, 5) // => 125
roundTo(128, 5) // => 130
roundTo(1.234, 0.1) // => 1.2000000000000002

// Data-last (in pipe)
pipe(
  price,
  roundTo(0.25)
)

// Real-world: Price quantization
const roundPrice = (price: number) =>
  pipe(price, roundTo(0.01)) // Round to nearest cent

// Real-world: Slider step values
const quantizeSlider = (value: number, step: number) =>
  roundTo(value, step)

See also round.

step

Rounds a number to the nearest step value.

Useful for pricing, UI sliders, or any value that should snap to specific increments.

Data-first
function step(value: number, stepSize: number): number
Data-last
function step(stepSize: number): (value: number) => number
Example
// Data-first
step(7, 5) // => 5
step(8, 5) // => 10
step(1.23, 0.25) // => 1.25
step(1.22, 0.25) // => 1.25

// Data-last (in pipe)
pipe(
  sliderValue,
  step(5)
)

// Real-world: Price rounding to .99
const roundToNinetyNine = (price: number) =>
  Math.ceil(price) - 0.01

// Step pricing (e.g., $5 increments)
const roundToFiveDollars = (price: number) =>
  step(price, 5)

See also round, clamp.

sum

Calculates the sum of an array of numbers.

Returns 0 for an empty array.

Signature
function sum(values: readonly number[]): number
Example
sum([1, 2, 3, 4]) // => 10
sum([]) // => 0
sum([-1, 1]) // => 0

// Floating-point rounding still applies
sum([0.1, 0.2, 0.3]) // => 0.6000000000000001

// Real-world: Shopping cart total
const cartTotal = (items: CartItem[]) =>
  sum(items.map(item => item.price * item.quantity))

See also average.

toBytes

Formats a number as a byte size string (KB, MB, GB, etc.).

Automatically selects the appropriate unit based on the value. Supports both binary (1024) and decimal (1000) bases.

Data-first
function toBytes(value: number, options?: ByteOptions | undefined): string
Data-last
function toBytes(options?: ByteOptions | undefined): (value: number) => string
Example
// Data-first (binary by default)
toBytes(1024) // => "1.00 KB"
toBytes(1048576) // => "1.00 MB"
toBytes(1536, { decimals: 0 }) // => "2 KB"
toBytes(1000, { base: 'decimal' }) // => "1.00 KB"

// Data-last (in pipe)
pipe(
  fileSize,
  toBytes({ decimals: 1 })
)

// Real-world: File size display
const displayFileSize = (bytes: number) =>
  bytes < 1024 ? `${bytes} B` : toBytes(bytes, { decimals: 1 })

See also fromBytes.

toCompact

Formats a number in compact notation (K, M, B, T).

Uses the Intl.NumberFormat API with compact notation. For example, 1000 becomes "1K", 1000000 becomes "1M".

Data-first
function toCompact(value: number, options?: CompactOptions | undefined): string
Data-last
function toCompact(options?: CompactOptions | undefined): (value: number) => string
Example
// Data-first
toCompact(1000) // => "1K"
toCompact(1500) // => "1.5K"
toCompact(1000000) // => "1M"
toCompact(1234567, { digits: 2 }) // => "1.23M"
toCompact(1000, { notation: 'long' }) // => "1 thousand"

// Data-last (in pipe)
pipe(
  followerCount,
  toCompact({ digits: 1 })
)

// Real-world: Social media metrics
const displayCount = (count: number) =>
  count < 10000 ? count.toString() : toCompact(count)

See also format.

toCurrency

Formats a number as currency with the specified currency code and locale.

Uses the Intl.NumberFormat API for locale-aware currency formatting.

Data-first
function toCurrency(value: number, options: CurrencyOptions): string
Data-last
function toCurrency(options: CurrencyOptions): (value: number) => string
Example
// Data-first
toCurrency(1234.56, { currency: 'USD' }) // => "$1,234.56"
toCurrency(1234.56, { currency: 'EUR', locale: 'de-DE' }) // => "1.234,56\u00a0€"
toCurrency(1234.56, { currency: 'GBP' }) // => "£1,234.56"
toCurrency(1234, { currency: 'JPY' }) // => "¥1,234" (no decimals)

// Data-last (in pipe)
pipe(
  orderTotal,
  toCurrency({ currency: 'USD' })
)

// Real-world: Stripe checkout
const displayPrice = (amountInCents: number) =>
  pipe(
    amountInCents,
    (n) => n / 100,
    toCurrency({ currency: 'USD' })
  )

See also format.

toOrdinal

Formats a number as an ordinal (1st, 2nd, 3rd, etc.).

Adds the appropriate English ordinal suffix to the number.

Data-first
function toOrdinal(value: number): string
Data-last
function toOrdinal(): (value: number) => string
Example
// Data-first
toOrdinal(1) // => "1st"
toOrdinal(2) // => "2nd"
toOrdinal(3) // => "3rd"
toOrdinal(4) // => "4th"
toOrdinal(11) // => "11th"
toOrdinal(21) // => "21st"
toOrdinal(42) // => "42nd"

// Data-last (in pipe)
pipe(
  position,
  toOrdinal
)

// Real-world: Leaderboard
const displayRank = (rank: number) =>
  `You placed ${toOrdinal(rank)} out of ${total}`

toPercent

Formats a number as a percentage.

Multiplies the value by 100 and adds a percent sign. For example, 0.25 becomes "25%", 1.5 becomes "150%".

Data-first
function toPercent(value: number, decimals?: number | undefined): string
Data-last
function toPercent(decimals?: number | undefined): (value: number) => string
Example
// Data-first (requires 2 args)
toPercent(0.25, 0) // => "25%"
toPercent(0.1234, 2) // => "12.34%"
toPercent(1.5, 0) // => "150%"
toPercent(0.333333, 1) // => "33.3%"

// Data-last (in pipe)
pipe(
  conversionRate,
  toPercent(2)
)

// Real-world: Analytics dashboard
const successRate = (successful: number, total: number) =>
  pipe(
    successful / total,
    toPercent(1)
  )

See also percentage.

toPrecision

Formats a number to a specified number of significant digits.

Uses toPrecision() to format the number with the specified precision.

Data-first
function toPrecision(value: number, digits: number): string
Data-last
function toPrecision(digits: number): (value: number) => string
Example
// Data-first
toPrecision(123.456, 4) // => "123.5"
toPrecision(0.0012345, 3) // => "0.00123"
toPrecision(1234567, 3) // => "1.23e+6"

// Data-last (in pipe)
pipe(
  measurementValue,
  toPrecision(3)
)

See also round.

Types

ByteOptions

Options for byte formatting.

Signature
interface ByteOptions {
  readonly decimals?: number

  readonly base?: 'binary' | 'decimal'
}

ByteUnit

Byte unit types.

Signature
type ByteUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB'

CompactOptions

Options for compact number formatting (1K, 1M, etc.).

Signature
interface CompactOptions {
  readonly locale?: string

  readonly digits?: number

  readonly notation?: 'short' | 'long'
}

CurrencyOptions

Options for currency formatting.

Signature
interface CurrencyOptions {
  readonly currency: string

  readonly locale?: string

  readonly showSymbol?: boolean

  readonly decimals?: number
}

FormatOptions

Options for formatting numbers.

Signature
interface FormatOptions {
  readonly decimals?: number

  readonly useGrouping?: boolean

  readonly locale?: string

  readonly minimumFractionDigits?: number

  readonly maximumFractionDigits?: number
}

InvalidOperationError

Error thrown when a number operation is invalid.

Signature
interface InvalidOperationError {
  readonly code: 'INVALID_OPERATION'
  readonly message: string
  readonly value?: number
}

ParseError

Error thrown when number parsing fails.

Signature
interface ParseError {
  readonly code: 'PARSE_ERROR'
  readonly message: string
  readonly input: string
}

On this page