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.
function average(values: readonly number[]): Option<number>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.
function clamp(value: number, min: number, max: number): numberfunction clamp(min: number, max: number): (value: number) => number// 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.
function format(value: number, options?: FormatOptions | undefined): stringfunction format(options?: FormatOptions | undefined): (value: number) => string// 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 priceSee 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.
function fromBytes(input: string, base?: "binary" | "decimal"): Result<number, ParseError>Fails with ParseError.
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".
function fromCurrency(input: string): Result<number, ParseError>Fails with ParseError.
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.
function fromString(input: string): Result<number, ParseError>Fails with ParseError.
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.
function inRange(value: number, min: number, max: number): booleanfunction inRange(min: number, max: number): (value: number) => boolean// 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.
function interpolate(from: number, to: number, progress: number): numberfunction interpolate(from: number, to: number): (progress: number) => number// 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).
function isFinite(value: number): booleanisFinite(42) // => true
isFinite(42.5) // => true
isFinite(Infinity) // => false
isFinite(-Infinity) // => false
isFinite(NaN) // => falseSee 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).
function isInteger(value: number): booleanisInteger(42) // => true
isInteger(42.5) // => false
isInteger(Number.MAX_SAFE_INTEGER) // => true
isInteger(Number.MAX_SAFE_INTEGER + 1) // => falseSee also isFinite, inRange.
isNegative
Type guard to check if a number is negative.
Returns true if the value is less than zero.
function isNegative(value: number): booleanisNegative(-5) // => true
isNegative(0) // => false
isNegative(42) // => falseSee also isPositive.
isPositive
Type guard to check if a number is positive.
Returns true if the value is greater than zero.
function isPositive(value: number): booleanimport { isPositive } from '@ontologyio/receta/number'
isPositive(42) // => true
isPositive(0) // => false
isPositive(-5) // => falseSee 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.
function normalize(value: number, min: number, max: number): numberfunction normalize(min: number, max: number): (value: number) => number// 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.
function parseFormattedNumber(input: string): Result<number, ParseError>Fails with ParseError.
// 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.89See 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.
function percentage(value: number, total: number): numberfunction percentage(total: number): (value: number) => number// 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.
function random(max: number): numberfunction random(min: number, max: number): numberrandom(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.
function ratio(a: number, b: number): numberfunction ratio(b: number): (a: number) => number// 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).
function round(value: number, decimals?: number | undefined): numberfunction round(decimals?: number | undefined): (value: number) => number// 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.
function roundTo(value: number, step: number): numberfunction roundTo(step: number): (value: number) => number// 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.
function step(value: number, stepSize: number): numberfunction step(stepSize: number): (value: number) => number// 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.
function sum(values: readonly number[]): numbersum([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.
function toBytes(value: number, options?: ByteOptions | undefined): stringfunction toBytes(options?: ByteOptions | undefined): (value: number) => string// 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".
function toCompact(value: number, options?: CompactOptions | undefined): stringfunction toCompact(options?: CompactOptions | undefined): (value: number) => string// 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.
function toCurrency(value: number, options: CurrencyOptions): stringfunction toCurrency(options: CurrencyOptions): (value: number) => string// 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.
function toOrdinal(value: number): stringfunction toOrdinal(): (value: number) => string// 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%".
function toPercent(value: number, decimals?: number | undefined): stringfunction toPercent(decimals?: number | undefined): (value: number) => string// 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.
function toPrecision(value: number, digits: number): stringfunction toPrecision(digits: number): (value: number) => string// 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.
interface ByteOptions {
readonly decimals?: number
readonly base?: 'binary' | 'decimal'
}ByteUnit
Byte unit types.
type ByteUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB'CompactOptions
Options for compact number formatting (1K, 1M, etc.).
interface CompactOptions {
readonly locale?: string
readonly digits?: number
readonly notation?: 'short' | 'long'
}CurrencyOptions
Options for currency formatting.
interface CurrencyOptions {
readonly currency: string
readonly locale?: string
readonly showSymbol?: boolean
readonly decimals?: number
}FormatOptions
Options for formatting numbers.
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.
interface InvalidOperationError {
readonly code: 'INVALID_OPERATION'
readonly message: string
readonly value?: number
}ParseError
Error thrown when number parsing fails.
interface ParseError {
readonly code: 'PARSE_ERROR'
readonly message: string
readonly input: string
}