A practical functional library for JavaScript programmers.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Ramda provides 58 functions for mathematical operations and logical reasoning. These include 13 math functions for numerical computations, 19 logic functions for conditional logic, and 26 relation functions for comparisons and boolean operations.
// Addition (curried)
R.add(2, 3); // => 5
R.add(7)(10); // => 17
const increment = R.add(1);
increment(5); // => 6
// Subtraction (a - b)
R.subtract(10, 3); // => 7
R.subtract(R.__, 5)(12); // => 7 (12 - 5)
const minus10 = R.subtract(R.__, 10);
minus10(25); // => 15 (25 - 10)
// Multiplication
R.multiply(3, 4); // => 12
const double = R.multiply(2);
double(8); // => 16
// Division (a / b)
R.divide(10, 2); // => 5
const half = R.divide(R.__, 2);
half(20); // => 10
const reciprocal = R.divide(1);
reciprocal(4); // => 0.25 (1/4)// Increment/Decrement
R.inc(5); // => 6
R.dec(5); // => 4
// Useful in transformations
R.map(R.inc, [1, 2, 3]); // => [2, 3, 4]
// Modulo operations
R.modulo(17, 3); // => 2 (JavaScript-style: 17 % 3)
R.modulo(-17, 3); // => -2 (preserves sign)
// Mathematical modulo (always positive)
R.mathMod(17, 5); // => 2
R.mathMod(-17, 5); // => 3 (mathematical modulo)
const isEven = R.pipe(R.modulo(R.__, 2), R.equals(0));
isEven(4); // => true
isEven(5); // => false
// Negation
R.negate(42); // => -42
R.negate(-42); // => 42
const invertSigns = R.map(R.negate);
invertSigns([1, -2, 3]); // => [-1, 2, -3]// Sum array elements
R.sum([1, 2, 3, 4]); // => 10
R.sum([]); // => 0
// Product of array elements
R.product([2, 4, 6]); // => 48
R.product([2, 4, 6, 0]); // => 0
R.product([]); // => 1
// Mean (average)
R.mean([2, 7, 9]); // => 6
R.mean([1, 2, 3, 4, 5]); // => 3
R.mean([]); // => NaN
// Median (middle value)
R.median([2, 9, 7]); // => 7
R.median([7, 2, 10, 9]); // => 8 (average of middle two)
R.median([]); // => NaN
// Real-world example: statistical analysis
const analyzeScores = R.applySpec({
count: R.length,
total: R.sum,
average: R.mean,
median: R.median,
range: R.converge(R.subtract, [
R.apply(Math.max),
R.apply(Math.min)
])
});
const scores = [85, 92, 78, 96, 88];
analyzeScores(scores);
// => {count: 5, total: 439, average: 87.8, median: 88, range: 18}// Greater than
R.gt(2, 1); // => true
R.gt(1, 2); // => false
R.gt('z', 'a'); // => true (lexicographic)
const isPositive = R.gt(R.__, 0);
isPositive(5); // => true
isPositive(-3); // => false
// Greater than or equal
R.gte(2, 2); // => true
R.gte(3, 2); // => true
R.gte(1, 2); // => false
// Less than
R.lt(1, 2); // => true
R.lt(2, 1); // => false
const isBelowLimit = R.lt(R.__, 100);
isBelowLimit(75); // => true
// Less than or equal
R.lte(2, 2); // => true
R.lte(1, 2); // => true
R.lte(3, 2); // => false
// Min/Max
R.min(5, 3); // => 3
R.max(5, 3); // => 5
// With custom comparison function
R.minBy(R.length, 'cat', 'elephant'); // => 'cat'
R.maxBy(R.prop('age'),
{name: 'Alice', age: 30},
{name: 'Bob', age: 25}
); // => {name: 'Alice', age: 30}// Deep equality (R.equals)
R.equals(1, 1); // => true
R.equals([1, 2], [1, 2]); // => true
R.equals({a: 1}, {a: 1}); // => true
R.equals(NaN, NaN); // => true
// Handles circular references
const a = {}; a.self = a;
const b = {}; b.self = b;
R.equals(a, b); // => true
// Reference equality (identical memory location)
R.identical(1, 1); // => true
R.identical([], []); // => false (different objects)
R.identical(NaN, NaN); // => true
R.identical(0, -0); // => false
const obj = {};
R.identical(obj, obj); // => true
// Property equality
R.eqProps('name',
{name: 'John', age: 30},
{name: 'John', age: 25}
); // => true
// Equality by transformation
R.eqBy(Math.abs, 5, -5); // => true
R.eqBy(R.length, 'cat', 'dog'); // => true// Clamp value to range
R.clamp(1, 10, 15); // => 10 (clamped to max)
R.clamp(1, 10, -5); // => 1 (clamped to min)
R.clamp(1, 10, 5); // => 5 (within range)
const clampPercent = R.clamp(0, 100);
clampPercent(150); // => 100
clampPercent(-10); // => 0
// Property-based comparisons
const users = [
{name: 'Alice', score: 85},
{name: 'Bob', score: 92},
{name: 'Carol', score: 78}
];
const highScorer = R.reduce(R.maxBy(R.prop('score')), users[0], users);
// => {name: 'Bob', score: 92}// Logical AND (short-circuited)
R.and(true, true); // => true
R.and(true, false); // => false
R.and(false, 'anything'); // => false
R.and('truthy', 'value'); // => 'value'
// Logical OR (short-circuited)
R.or(true, false); // => true
R.or(false, 'default'); // => 'default'
R.or('first', 'second'); // => 'first'
// Logical NOT
R.not(true); // => false
R.not(false); // => true
R.not('truthy'); // => false
R.not(''); // => true
R.not(0); // => true
// XOR (exclusive or)
R.xor(true, false); // => true
R.xor(true, true); // => false
R.xor(false, false); // => false
// Create negated function (complement)
const isEven = x => x % 2 === 0;
const isOdd = R.complement(isEven);
isOdd(3); // => true
isOdd(4); // => false
// Use in filtering
const nonEmptyStrings = R.filter(R.complement(R.isEmpty), ['', 'hello', '', 'world']);
// => ['hello', 'world']
// Real-world example: feature flags
const hasFeature = R.both(
R.prop('enabled'),
R.pipe(R.prop('userLevel'), R.gte(R.__, 'premium'))
);// Both predicates must be true
const isAdultUser = R.both(
R.propSatisfies(R.gte(R.__, 18), 'age'),
R.propEq(true, 'active')
);
const user1 = {age: 25, active: true};
const user2 = {age: 16, active: true};
isAdultUser(user1); // => true
isAdultUser(user2); // => false
// Either predicate can be true
const isSpecialUser = R.either(
R.propEq('admin', 'role'),
R.propSatisfies(R.gt(R.__, 1000), 'points')
);
const admin = {role: 'admin', points: 100};
const vip = {role: 'user', points: 1500};
const regular = {role: 'user', points: 50};
isSpecialUser(admin); // => true (is admin)
isSpecialUser(vip); // => true (high points)
isSpecialUser(regular); // => false
// All predicates must pass
const isValidProduct = R.allPass([
R.has('name'),
R.has('price'),
R.propSatisfies(R.gt(R.__, 0), 'price'),
R.propSatisfies(R.complement(R.isEmpty), 'name')
]);
// Any predicate can pass
const isSearchable = R.anyPass([
R.has('title'),
R.has('description'),
R.has('tags')
]);
// Complement (logical NOT for predicates)
const isNotEmpty = R.complement(R.isEmpty);
const isNotNil = R.complement(R.isNil);
isNotEmpty([1, 2, 3]); // => true
isNotNil('value'); // => true// If-then-else logic
const processValue = R.ifElse(
R.is(Number), // condition
R.multiply(2), // if true: double it
R.always('Not a number') // if false: return message
);
processValue(5); // => 10
processValue('hello'); // => 'Not a number'
// Multi-condition logic with cond
const categorizeScore = R.cond([
[R.gte(R.__, 90), R.always('A')],
[R.gte(R.__, 80), R.always('B')],
[R.gte(R.__, 70), R.always('C')],
[R.gte(R.__, 60), R.always('D')],
[R.T, R.always('F')] // default case
]);
categorizeScore(95); // => 'A'
categorizeScore(75); // => 'C'
categorizeScore(45); // => 'F'
// When/Unless - conditional transformations
const processPositive = R.when(
R.gt(R.__, 0), // condition: is positive
R.multiply(10) // transformation: multiply by 10
);
processPositive(5); // => 50
processPositive(-3); // => -3 (unchanged)
const avoidNegative = R.unless(
R.gt(R.__, 0), // condition: is positive
R.always(0) // transformation: set to 0
);
avoidNegative(5); // => 5 (unchanged)
avoidNegative(-3); // => 0// Default value for null/undefined/NaN
R.defaultTo(42, null); // => 42
R.defaultTo(42, undefined); // => 42
R.defaultTo(42, NaN); // => 42
R.defaultTo(42, 5); // => 5
R.defaultTo(42, false); // => false (not null/undefined/NaN)
// Safe property access with defaults
const getUserName = R.pipe(
R.prop('user'),
R.prop('name'),
R.defaultTo('Anonymous')
);
getUserName({user: {name: 'Alice'}}); // => 'Alice'
getUserName({user: {}}); // => 'Anonymous'
getUserName({}); // => 'Anonymous'
// Chain of fallbacks
const getDisplayName = R.pipe(
R.anyPass([R.prop('displayName'), R.prop('username'), R.prop('email')]),
R.defaultTo('User')
);// Test conditions on data
const isValidEmail = R.allPass([
R.is(String),
R.test(/@/),
R.test(/\./),
R.complement(R.isEmpty)
]);
isValidEmail('user@example.com'); // => true
isValidEmail('invalid-email'); // => false
// Property validation
R.propSatisfies(R.gt(R.__, 0), 'age', {age: 25}); // => true
R.propSatisfies(R.is(String), 'name', {name: 'John'}); // => true
// Path validation for nested objects
R.pathSatisfies(R.is(Number), ['profile', 'age'], {
profile: {age: 30}
}); // => true
// Complex validation example
const validateUser = R.where({
email: isValidEmail,
age: R.both(R.is(Number), R.gte(R.__, 13)),
name: R.both(R.is(String), R.complement(R.isEmpty)),
terms: R.equals(true)
});
const user = {
email: 'user@example.com',
age: 25,
name: 'John Doe',
terms: true
};
validateUser(user); // => true// Until condition is met
const collatz = R.until(
R.equals(1), // stop when value equals 1
R.ifElse(
R.modulo(R.__, 2), // if odd
R.pipe(R.multiply(3), R.add(1)), // 3n + 1
R.divide(R.__, 2) // else n/2
)
);
collatz(7); // => 1 (goes through: 7→22→11→34→17→52→26→13→40→20→10→5→16→8→4→2→1)
// Iterative application
const approach = R.until(
R.lt(R.__, 0.001), // stop when difference < 0.001
R.multiply(0.9) // multiply by 0.9 each iteration
);
approach(10); // => 0.0008748... (approaches 0)These math and logic functions provide the building blocks for complex calculations, data validation, conditional processing, and algorithmic operations within Ramda's functional programming paradigm.
Install with Tessl CLI
npx tessl i tessl/npm-ramda