0
# Math & Logic Functions
1
2
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.
3
4
## Mathematical Operations
5
6
### Basic Arithmetic
7
8
```javascript { .api }
9
// Addition (curried)
10
R.add(2, 3); // => 5
11
R.add(7)(10); // => 17
12
13
const increment = R.add(1);
14
increment(5); // => 6
15
16
// Subtraction (a - b)
17
R.subtract(10, 3); // => 7
18
R.subtract(R.__, 5)(12); // => 7 (12 - 5)
19
20
const minus10 = R.subtract(R.__, 10);
21
minus10(25); // => 15 (25 - 10)
22
23
// Multiplication
24
R.multiply(3, 4); // => 12
25
const double = R.multiply(2);
26
double(8); // => 16
27
28
// Division (a / b)
29
R.divide(10, 2); // => 5
30
const half = R.divide(R.__, 2);
31
half(20); // => 10
32
33
const reciprocal = R.divide(1);
34
reciprocal(4); // => 0.25 (1/4)
35
```
36
37
### Advanced Math Operations
38
39
```javascript { .api }
40
// Increment/Decrement
41
R.inc(5); // => 6
42
R.dec(5); // => 4
43
44
// Useful in transformations
45
R.map(R.inc, [1, 2, 3]); // => [2, 3, 4]
46
47
// Modulo operations
48
R.modulo(17, 3); // => 2 (JavaScript-style: 17 % 3)
49
R.modulo(-17, 3); // => -2 (preserves sign)
50
51
// Mathematical modulo (always positive)
52
R.mathMod(17, 5); // => 2
53
R.mathMod(-17, 5); // => 3 (mathematical modulo)
54
55
const isEven = R.pipe(R.modulo(R.__, 2), R.equals(0));
56
isEven(4); // => true
57
isEven(5); // => false
58
59
// Negation
60
R.negate(42); // => -42
61
R.negate(-42); // => 42
62
63
const invertSigns = R.map(R.negate);
64
invertSigns([1, -2, 3]); // => [-1, 2, -3]
65
```
66
67
### Array Aggregations
68
69
```javascript { .api }
70
// Sum array elements
71
R.sum([1, 2, 3, 4]); // => 10
72
R.sum([]); // => 0
73
74
// Product of array elements
75
R.product([2, 4, 6]); // => 48
76
R.product([2, 4, 6, 0]); // => 0
77
R.product([]); // => 1
78
79
// Mean (average)
80
R.mean([2, 7, 9]); // => 6
81
R.mean([1, 2, 3, 4, 5]); // => 3
82
R.mean([]); // => NaN
83
84
// Median (middle value)
85
R.median([2, 9, 7]); // => 7
86
R.median([7, 2, 10, 9]); // => 8 (average of middle two)
87
R.median([]); // => NaN
88
89
// Real-world example: statistical analysis
90
const analyzeScores = R.applySpec({
91
count: R.length,
92
total: R.sum,
93
average: R.mean,
94
median: R.median,
95
range: R.converge(R.subtract, [
96
R.apply(Math.max),
97
R.apply(Math.min)
98
])
99
});
100
101
const scores = [85, 92, 78, 96, 88];
102
analyzeScores(scores);
103
// => {count: 5, total: 439, average: 87.8, median: 88, range: 18}
104
```
105
106
## Comparison Functions
107
108
### Basic Comparisons
109
110
```javascript { .api }
111
// Greater than
112
R.gt(2, 1); // => true
113
R.gt(1, 2); // => false
114
R.gt('z', 'a'); // => true (lexicographic)
115
116
const isPositive = R.gt(R.__, 0);
117
isPositive(5); // => true
118
isPositive(-3); // => false
119
120
// Greater than or equal
121
R.gte(2, 2); // => true
122
R.gte(3, 2); // => true
123
R.gte(1, 2); // => false
124
125
// Less than
126
R.lt(1, 2); // => true
127
R.lt(2, 1); // => false
128
129
const isBelowLimit = R.lt(R.__, 100);
130
isBelowLimit(75); // => true
131
132
// Less than or equal
133
R.lte(2, 2); // => true
134
R.lte(1, 2); // => true
135
R.lte(3, 2); // => false
136
137
// Min/Max
138
R.min(5, 3); // => 3
139
R.max(5, 3); // => 5
140
141
// With custom comparison function
142
R.minBy(R.length, 'cat', 'elephant'); // => 'cat'
143
R.maxBy(R.prop('age'),
144
{name: 'Alice', age: 30},
145
{name: 'Bob', age: 25}
146
); // => {name: 'Alice', age: 30}
147
```
148
149
### Equality and Identity
150
151
```javascript { .api }
152
// Deep equality (R.equals)
153
R.equals(1, 1); // => true
154
R.equals([1, 2], [1, 2]); // => true
155
R.equals({a: 1}, {a: 1}); // => true
156
R.equals(NaN, NaN); // => true
157
158
// Handles circular references
159
const a = {}; a.self = a;
160
const b = {}; b.self = b;
161
R.equals(a, b); // => true
162
163
// Reference equality (identical memory location)
164
R.identical(1, 1); // => true
165
R.identical([], []); // => false (different objects)
166
R.identical(NaN, NaN); // => true
167
R.identical(0, -0); // => false
168
169
const obj = {};
170
R.identical(obj, obj); // => true
171
172
// Property equality
173
R.eqProps('name',
174
{name: 'John', age: 30},
175
{name: 'John', age: 25}
176
); // => true
177
178
// Equality by transformation
179
R.eqBy(Math.abs, 5, -5); // => true
180
R.eqBy(R.length, 'cat', 'dog'); // => true
181
```
182
183
### Range and Boundary Operations
184
185
```javascript { .api }
186
// Clamp value to range
187
R.clamp(1, 10, 15); // => 10 (clamped to max)
188
R.clamp(1, 10, -5); // => 1 (clamped to min)
189
R.clamp(1, 10, 5); // => 5 (within range)
190
191
const clampPercent = R.clamp(0, 100);
192
clampPercent(150); // => 100
193
clampPercent(-10); // => 0
194
195
// Property-based comparisons
196
const users = [
197
{name: 'Alice', score: 85},
198
{name: 'Bob', score: 92},
199
{name: 'Carol', score: 78}
200
];
201
202
const highScorer = R.reduce(R.maxBy(R.prop('score')), users[0], users);
203
// => {name: 'Bob', score: 92}
204
```
205
206
## Logic Functions
207
208
### Basic Boolean Operations
209
210
```javascript { .api }
211
// Logical AND (short-circuited)
212
R.and(true, true); // => true
213
R.and(true, false); // => false
214
R.and(false, 'anything'); // => false
215
R.and('truthy', 'value'); // => 'value'
216
217
// Logical OR (short-circuited)
218
R.or(true, false); // => true
219
R.or(false, 'default'); // => 'default'
220
R.or('first', 'second'); // => 'first'
221
222
// Logical NOT
223
R.not(true); // => false
224
R.not(false); // => true
225
R.not('truthy'); // => false
226
R.not(''); // => true
227
R.not(0); // => true
228
229
// XOR (exclusive or)
230
R.xor(true, false); // => true
231
R.xor(true, true); // => false
232
R.xor(false, false); // => false
233
234
// Create negated function (complement)
235
const isEven = x => x % 2 === 0;
236
const isOdd = R.complement(isEven);
237
isOdd(3); // => true
238
isOdd(4); // => false
239
240
// Use in filtering
241
const nonEmptyStrings = R.filter(R.complement(R.isEmpty), ['', 'hello', '', 'world']);
242
// => ['hello', 'world']
243
244
// Real-world example: feature flags
245
const hasFeature = R.both(
246
R.prop('enabled'),
247
R.pipe(R.prop('userLevel'), R.gte(R.__, 'premium'))
248
);
249
```
250
251
### Predicate Combinators
252
253
```javascript { .api }
254
// Both predicates must be true
255
const isAdultUser = R.both(
256
R.propSatisfies(R.gte(R.__, 18), 'age'),
257
R.propEq(true, 'active')
258
);
259
260
const user1 = {age: 25, active: true};
261
const user2 = {age: 16, active: true};
262
263
isAdultUser(user1); // => true
264
isAdultUser(user2); // => false
265
266
// Either predicate can be true
267
const isSpecialUser = R.either(
268
R.propEq('admin', 'role'),
269
R.propSatisfies(R.gt(R.__, 1000), 'points')
270
);
271
272
const admin = {role: 'admin', points: 100};
273
const vip = {role: 'user', points: 1500};
274
const regular = {role: 'user', points: 50};
275
276
isSpecialUser(admin); // => true (is admin)
277
isSpecialUser(vip); // => true (high points)
278
isSpecialUser(regular); // => false
279
280
// All predicates must pass
281
const isValidProduct = R.allPass([
282
R.has('name'),
283
R.has('price'),
284
R.propSatisfies(R.gt(R.__, 0), 'price'),
285
R.propSatisfies(R.complement(R.isEmpty), 'name')
286
]);
287
288
// Any predicate can pass
289
const isSearchable = R.anyPass([
290
R.has('title'),
291
R.has('description'),
292
R.has('tags')
293
]);
294
295
// Complement (logical NOT for predicates)
296
const isNotEmpty = R.complement(R.isEmpty);
297
const isNotNil = R.complement(R.isNil);
298
299
isNotEmpty([1, 2, 3]); // => true
300
isNotNil('value'); // => true
301
```
302
303
### Conditional Logic
304
305
```javascript { .api }
306
// If-then-else logic
307
const processValue = R.ifElse(
308
R.is(Number), // condition
309
R.multiply(2), // if true: double it
310
R.always('Not a number') // if false: return message
311
);
312
313
processValue(5); // => 10
314
processValue('hello'); // => 'Not a number'
315
316
// Multi-condition logic with cond
317
const categorizeScore = R.cond([
318
[R.gte(R.__, 90), R.always('A')],
319
[R.gte(R.__, 80), R.always('B')],
320
[R.gte(R.__, 70), R.always('C')],
321
[R.gte(R.__, 60), R.always('D')],
322
[R.T, R.always('F')] // default case
323
]);
324
325
categorizeScore(95); // => 'A'
326
categorizeScore(75); // => 'C'
327
categorizeScore(45); // => 'F'
328
329
// When/Unless - conditional transformations
330
const processPositive = R.when(
331
R.gt(R.__, 0), // condition: is positive
332
R.multiply(10) // transformation: multiply by 10
333
);
334
335
processPositive(5); // => 50
336
processPositive(-3); // => -3 (unchanged)
337
338
const avoidNegative = R.unless(
339
R.gt(R.__, 0), // condition: is positive
340
R.always(0) // transformation: set to 0
341
);
342
343
avoidNegative(5); // => 5 (unchanged)
344
avoidNegative(-3); // => 0
345
```
346
347
### Default Values and Fallbacks
348
349
```javascript { .api }
350
// Default value for null/undefined/NaN
351
R.defaultTo(42, null); // => 42
352
R.defaultTo(42, undefined); // => 42
353
R.defaultTo(42, NaN); // => 42
354
R.defaultTo(42, 5); // => 5
355
R.defaultTo(42, false); // => false (not null/undefined/NaN)
356
357
// Safe property access with defaults
358
const getUserName = R.pipe(
359
R.prop('user'),
360
R.prop('name'),
361
R.defaultTo('Anonymous')
362
);
363
364
getUserName({user: {name: 'Alice'}}); // => 'Alice'
365
getUserName({user: {}}); // => 'Anonymous'
366
getUserName({}); // => 'Anonymous'
367
368
// Chain of fallbacks
369
const getDisplayName = R.pipe(
370
R.anyPass([R.prop('displayName'), R.prop('username'), R.prop('email')]),
371
R.defaultTo('User')
372
);
373
```
374
375
### Validation and Testing
376
377
```javascript { .api }
378
// Test conditions on data
379
const isValidEmail = R.allPass([
380
R.is(String),
381
R.test(/@/),
382
R.test(/\./),
383
R.complement(R.isEmpty)
384
]);
385
386
isValidEmail('user@example.com'); // => true
387
isValidEmail('invalid-email'); // => false
388
389
// Property validation
390
R.propSatisfies(R.gt(R.__, 0), 'age', {age: 25}); // => true
391
R.propSatisfies(R.is(String), 'name', {name: 'John'}); // => true
392
393
// Path validation for nested objects
394
R.pathSatisfies(R.is(Number), ['profile', 'age'], {
395
profile: {age: 30}
396
}); // => true
397
398
// Complex validation example
399
const validateUser = R.where({
400
email: isValidEmail,
401
age: R.both(R.is(Number), R.gte(R.__, 13)),
402
name: R.both(R.is(String), R.complement(R.isEmpty)),
403
terms: R.equals(true)
404
});
405
406
const user = {
407
email: 'user@example.com',
408
age: 25,
409
name: 'John Doe',
410
terms: true
411
};
412
413
validateUser(user); // => true
414
```
415
416
### Loop-Like Operations
417
418
```javascript { .api }
419
// Until condition is met
420
const collatz = R.until(
421
R.equals(1), // stop when value equals 1
422
R.ifElse(
423
R.modulo(R.__, 2), // if odd
424
R.pipe(R.multiply(3), R.add(1)), // 3n + 1
425
R.divide(R.__, 2) // else n/2
426
)
427
);
428
429
collatz(7); // => 1 (goes through: 7→22→11→34→17→52→26→13→40→20→10→5→16→8→4→2→1)
430
431
// Iterative application
432
const approach = R.until(
433
R.lt(R.__, 0.001), // stop when difference < 0.001
434
R.multiply(0.9) // multiply by 0.9 each iteration
435
);
436
437
approach(10); // => 0.0008748... (approaches 0)
438
```
439
440
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.