0
# Object Functions
1
2
Ramda provides 57 comprehensive functions for working with objects and their properties. These functions enable immutable object manipulation, property access, and complex object transformations while maintaining functional programming principles.
3
4
## Property Access
5
6
### prop
7
Get a property value from an object.
8
9
```javascript { .api }
10
/**
11
* @param {String|Number} key - Property key to access
12
* @param {Object} obj - Object to read from
13
* @returns {*} Property value or undefined
14
*/
15
R.prop(key, obj)
16
17
const user = { name: 'John', age: 30, active: true };
18
19
R.prop('name', user); // => 'John'
20
R.prop('age', user); // => 30
21
R.prop('missing', user); // => undefined
22
23
// Curried usage for data extraction
24
const getName = R.prop('name');
25
const users = [{name: 'Alice'}, {name: 'Bob'}];
26
R.map(getName, users); // => ['Alice', 'Bob']
27
```
28
29
### props
30
Get multiple property values as an array.
31
32
```javascript { .api }
33
/**
34
* @param {Array} keys - Array of property keys
35
* @param {Object} obj - Object to read from
36
* @returns {Array} Array of property values
37
*/
38
R.props(keys, obj)
39
40
const user = { name: 'John', age: 30, city: 'NYC' };
41
42
R.props(['name', 'age'], user); // => ['John', 30]
43
R.props(['city', 'name'], user); // => ['NYC', 'John']
44
R.props(['missing', 'name'], user); // => [undefined, 'John']
45
46
// Extract specific fields from objects
47
const extractInfo = R.props(['id', 'name', 'email']);
48
const users = [{id: 1, name: 'Alice', email: 'alice@example.com', extra: 'data'}];
49
R.map(extractInfo, users); // => [[1, 'Alice', 'alice@example.com']]
50
```
51
52
### path
53
Access nested properties safely.
54
55
```javascript { .api }
56
/**
57
* @param {Array} pathArray - Array of keys for nested access
58
* @param {Object} obj - Object to traverse
59
* @returns {*} Nested property value or undefined
60
*/
61
R.path(pathArray, obj)
62
63
const user = {
64
profile: {
65
personal: { name: 'John', age: 30 },
66
contact: { email: 'john@example.com' }
67
}
68
};
69
70
R.path(['profile', 'personal', 'name'], user); // => 'John'
71
R.path(['profile', 'contact', 'email'], user); // => 'john@example.com'
72
R.path(['profile', 'missing', 'field'], user); // => undefined
73
R.path(['a', 'b'], { a: { b: 2 } }); // => 2
74
75
// Safe access prevents errors
76
R.path(['deep', 'nested', 'prop'], {}); // => undefined (no error)
77
```
78
79
### pathOr
80
Access nested properties with default value.
81
82
```javascript { .api }
83
/**
84
* @param {*} defaultValue - Value to return if path doesn't exist
85
* @param {Array} pathArray - Array of keys for nested access
86
* @param {Object} obj - Object to traverse
87
* @returns {*} Nested property value or default
88
*/
89
R.pathOr(defaultValue, pathArray, obj)
90
91
const config = { api: { timeout: 5000 } };
92
93
R.pathOr(3000, ['api', 'timeout'], config); // => 5000
94
R.pathOr(3000, ['api', 'retries'], config); // => 3000 (default)
95
R.pathOr('Unknown', ['user', 'name'], {}); // => 'Unknown'
96
```
97
98
## Object Copying and Cloning
99
100
### clone
101
Create a deep copy of an object or array.
102
103
```javascript { .api }
104
/**
105
* @param {*} value - Value to clone (object, array, or primitive)
106
* @returns {*} Deep copy of the value
107
*/
108
R.clone(value)
109
110
const original = {
111
name: 'John',
112
scores: [85, 90, 78],
113
profile: { age: 30, active: true }
114
};
115
116
const copied = R.clone(original);
117
copied.scores.push(95); // Original scores unchanged
118
copied.profile.age = 31; // Original profile unchanged
119
120
// Safe for nested structures
121
const nestedArray = [[1, 2], [3, 4]];
122
const clonedArray = R.clone(nestedArray);
123
clonedArray[0].push(3); // Original array unchanged
124
125
// Handles various data types
126
R.clone([1, 2, 3]); // => [1, 2, 3] (new array)
127
R.clone({a: 1}); // => {a: 1} (new object)
128
R.clone('hello'); // => 'hello' (primitives return as-is)
129
```
130
131
## Property Modification
132
133
### assoc
134
Set a property (immutable).
135
136
```javascript { .api }
137
/**
138
* @param {String|Number} key - Property key to set
139
* @param {*} value - Value to assign
140
* @param {Object} obj - Object to modify
141
* @returns {Object} New object with property set
142
*/
143
R.assoc(key, value, obj)
144
145
const user = { name: 'John', age: 30 };
146
147
R.assoc('age', 31, user); // => { name: 'John', age: 31 }
148
R.assoc('city', 'NYC', user); // => { name: 'John', age: 30, city: 'NYC' }
149
150
// Original object unchanged
151
console.log(user); // => { name: 'John', age: 30 }
152
153
// Curried usage for object updates
154
const addTimestamp = R.assoc('timestamp', Date.now());
155
const withTimestamp = addTimestamp({ data: 'value' });
156
```
157
158
### assocPath
159
Set nested property (immutable).
160
161
```javascript { .api }
162
/**
163
* @param {Array} pathArray - Array of keys for nested access
164
* @param {*} value - Value to set at path
165
* @param {Object} obj - Object to modify
166
* @returns {Object} New object with nested property set
167
*/
168
R.assocPath(pathArray, value, obj)
169
170
const user = { profile: { name: 'John', settings: { theme: 'dark' } } };
171
172
R.assocPath(['profile', 'name'], 'Jane', user);
173
// => { profile: { name: 'Jane', settings: { theme: 'dark' } } }
174
175
R.assocPath(['profile', 'settings', 'theme'], 'light', user);
176
// => { profile: { name: 'John', settings: { theme: 'light' } } }
177
178
// Create missing intermediate objects
179
R.assocPath(['new', 'nested', 'prop'], 'value', {});
180
// => { new: { nested: { prop: 'value' } } }
181
```
182
183
### dissoc
184
Remove a property (immutable).
185
186
```javascript { .api }
187
/**
188
* @param {String} key - Property key to remove
189
* @param {Object} obj - Object to modify
190
* @returns {Object} New object without the property
191
*/
192
R.dissoc(key, obj)
193
194
const user = { name: 'John', age: 30, temp: 'remove-me' };
195
196
R.dissoc('temp', user); // => { name: 'John', age: 30 }
197
R.dissoc('missing', user); // => { name: 'John', age: 30, temp: 'remove-me' }
198
199
// Remove multiple properties
200
const removeFields = R.pipe(
201
R.dissoc('temp'),
202
R.dissoc('internal')
203
);
204
```
205
206
### dissocPath
207
Remove nested property (immutable).
208
209
```javascript { .api }
210
/**
211
* @param {Array} pathArray - Array of keys for nested access
212
* @param {Object} obj - Object to modify
213
* @returns {Object} New object without the nested property
214
*/
215
R.dissocPath(pathArray, obj)
216
217
const user = {
218
profile: {
219
name: 'John',
220
settings: { theme: 'dark', lang: 'en' }
221
}
222
};
223
224
R.dissocPath(['profile', 'settings', 'theme'], user);
225
// => { profile: { name: 'John', settings: { lang: 'en' } } }
226
```
227
228
## Object Transformation
229
230
### evolve
231
Transform object properties using functions.
232
233
```javascript { .api }
234
/**
235
* @param {Object} transformations - Object mapping keys to transformation functions
236
* @param {Object} obj - Object to transform
237
* @returns {Object} New object with properties transformed
238
*/
239
R.evolve(transformations, obj)
240
241
const user = { name: ' john ', age: '30', active: 'true' };
242
243
const transformations = {
244
name: R.pipe(R.trim, R.toUpper),
245
age: parseInt,
246
active: R.equals('true')
247
};
248
249
R.evolve(transformations, user);
250
// => { name: 'JOHN', age: 30, active: true }
251
252
// Nested transformations
253
const nestedTransform = {
254
profile: {
255
name: R.toUpper,
256
age: R.add(1)
257
}
258
};
259
260
R.evolve(nestedTransform, {
261
profile: { name: 'john', age: 30 },
262
other: 'unchanged'
263
});
264
// => { profile: { name: 'JOHN', age: 31 }, other: 'unchanged' }
265
```
266
267
### modify
268
Transform a specific property.
269
270
```javascript { .api }
271
/**
272
* @param {String|Number} key - Property key to modify
273
* @param {Function} fn - Transformation function
274
* @param {Object} obj - Object to modify
275
* @returns {Object} New object with property transformed
276
*/
277
R.modify(key, fn, obj)
278
279
const user = { name: 'john', age: 30, scores: [85, 90, 78] };
280
281
R.modify('name', R.toUpper, user); // => { name: 'JOHN', age: 30, scores: [85, 90, 78] }
282
R.modify('age', R.add(1), user); // => { name: 'john', age: 31, scores: [85, 90, 78] }
283
R.modify('scores', R.append(95), user); // => { name: 'john', age: 30, scores: [85, 90, 78, 95] }
284
```
285
286
### modifyPath
287
Transform a nested property.
288
289
```javascript { .api }
290
/**
291
* @param {Array} pathArray - Array of keys for nested access
292
* @param {Function} fn - Transformation function
293
* @param {Object} obj - Object to modify
294
* @returns {Object} New object with nested property transformed
295
*/
296
R.modifyPath(pathArray, fn, obj)
297
298
const state = {
299
user: {
300
profile: { name: 'john', visits: 5 }
301
}
302
};
303
304
R.modifyPath(['user', 'profile', 'name'], R.toUpper, state);
305
// => { user: { profile: { name: 'JOHN', visits: 5 } } }
306
307
R.modifyPath(['user', 'profile', 'visits'], R.inc, state);
308
// => { user: { profile: { name: 'john', visits: 6 } } }
309
```
310
311
## Object Merging
312
313
### merge
314
Shallow merge two objects.
315
316
```javascript { .api }
317
/**
318
* @param {Object} obj1 - First object
319
* @param {Object} obj2 - Second object (properties override obj1)
320
* @returns {Object} New merged object
321
*/
322
R.merge(obj1, obj2)
323
324
const defaults = { timeout: 5000, retries: 3 };
325
const config = { timeout: 8000, debug: true };
326
327
R.merge(defaults, config);
328
// => { timeout: 8000, retries: 3, debug: true }
329
330
// Curried usage for applying defaults
331
const withDefaults = R.merge({ theme: 'light', lang: 'en' });
332
withDefaults({ theme: 'dark' }); // => { theme: 'dark', lang: 'en' }
333
```
334
335
### mergeDeepLeft, mergeDeepRight
336
Deep merge objects with conflict resolution.
337
338
```javascript { .api }
339
const obj1 = {
340
a: 1,
341
nested: { x: 10, y: 20 }
342
};
343
344
const obj2 = {
345
b: 2,
346
nested: { x: 30, z: 40 }
347
};
348
349
// Left object takes precedence
350
R.mergeDeepLeft(obj1, obj2);
351
// => { a: 1, b: 2, nested: { x: 10, y: 20, z: 40 } }
352
353
// Right object takes precedence
354
R.mergeDeepRight(obj1, obj2);
355
// => { a: 1, b: 2, nested: { x: 30, y: 20, z: 40 } }
356
```
357
358
### mergeWith, mergeDeepWith
359
Merge objects with custom conflict resolution.
360
361
```javascript { .api }
362
/**
363
* @param {Function} conflictFn - Function to resolve conflicts (leftVal, rightVal) -> resolvedVal
364
* @param {Object} obj1 - First object
365
* @param {Object} obj2 - Second object
366
* @returns {Object} New merged object
367
*/
368
R.mergeWith(conflictFn, obj1, obj2)
369
370
const obj1 = { a: 1, b: [1, 2], c: 'hello' };
371
const obj2 = { a: 2, b: [3, 4], d: 'world' };
372
373
// Concatenate conflicting arrays, add numbers, take right for others
374
R.mergeWith(
375
(left, right) => {
376
if (Array.isArray(left)) return R.concat(left, right);
377
if (typeof left === 'number') return left + right;
378
return right;
379
},
380
obj1,
381
obj2
382
);
383
// => { a: 3, b: [1, 2, 3, 4], c: 'hello', d: 'world' }
384
```
385
386
## Object Introspection
387
388
### keys, values
389
Get object keys or values.
390
391
```javascript { .api }
392
const obj = { name: 'John', age: 30, active: true };
393
394
R.keys(obj); // => ['name', 'age', 'active']
395
R.values(obj); // => ['John', 30, true]
396
397
// keysIn and valuesIn include inherited properties
398
function Person(name) { this.name = name; }
399
Person.prototype.species = 'human';
400
const john = new Person('John');
401
402
R.keys(john); // => ['name']
403
R.keysIn(john); // => ['name', 'species']
404
```
405
406
### toPairs, toPairsIn
407
Convert object to key-value pairs.
408
409
```javascript { .api }
410
/**
411
* @param {Object} obj - Object to convert
412
* @returns {Array} Array of [key, value] pairs
413
*/
414
R.toPairs(obj)
415
416
const obj = { name: 'John', age: 30 };
417
418
R.toPairs(obj); // => [['name', 'John'], ['age', 30]]
419
420
// Convert back to object
421
R.fromPairs([['name', 'John'], ['age', 30]]); // => { name: 'John', age: 30 }
422
```
423
424
### has, hasIn, hasPath
425
Check property existence.
426
427
```javascript { .api }
428
const obj = { name: 'John', profile: { age: 30 } };
429
430
// Own properties only
431
R.has('name', obj); // => true
432
R.has('toString', obj); // => false
433
434
// Including inherited properties
435
R.hasIn('toString', obj); // => true
436
437
// Nested property existence
438
R.hasPath(['profile', 'age'], obj); // => true
439
R.hasPath(['profile', 'email'], obj); // => false
440
```
441
442
## Object Filtering and Selection
443
444
### pick, pickAll
445
Select specific properties.
446
447
```javascript { .api }
448
/**
449
* @param {Array} keys - Array of property keys to pick
450
* @param {Object} obj - Object to pick from
451
* @returns {Object} New object with only selected properties
452
*/
453
R.pick(keys, obj)
454
455
const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' };
456
457
R.pick(['id', 'name'], user); // => { id: 1, name: 'John' }
458
R.pick(['name', 'missing'], user); // => { name: 'John' }
459
460
// pickAll includes undefined for missing keys
461
R.pickAll(['name', 'missing'], user); // => { name: 'John', missing: undefined }
462
463
// Create safe user objects
464
const safeUser = R.pick(['id', 'name', 'email']);
465
safeUser(user); // => { id: 1, name: 'John', email: 'john@example.com' }
466
```
467
468
### omit
469
Exclude specific properties.
470
471
```javascript { .api }
472
/**
473
* @param {Array} keys - Array of property keys to omit
474
* @param {Object} obj - Object to omit from
475
* @returns {Object} New object without specified properties
476
*/
477
R.omit(keys, obj)
478
479
const user = { id: 1, name: 'John', password: 'secret', temp: 'data' };
480
481
R.omit(['password', 'temp'], user); // => { id: 1, name: 'John' }
482
483
// Remove sensitive data
484
const sanitize = R.omit(['password', 'apiKey', 'secret']);
485
sanitize(user); // => { id: 1, name: 'John' }
486
```
487
488
### pickBy
489
Select properties by predicate.
490
491
```javascript { .api }
492
/**
493
* @param {Function} predicate - Function to test values (value, key) -> Boolean
494
* @param {Object} obj - Object to filter
495
* @returns {Object} New object with properties that satisfy predicate
496
*/
497
R.pickBy(predicate, obj)
498
499
const data = { a: 1, b: 'hello', c: null, d: 42, e: undefined };
500
501
R.pickBy(R.is(Number), data); // => { a: 1, d: 42 }
502
R.pickBy(R.complement(R.isNil), data); // => { a: 1, b: 'hello', d: 42 }
503
R.pickBy((val, key) => key.length > 1, { a: 1, bb: 2, ccc: 3 }); // => { bb: 2, ccc: 3 }
504
```
505
506
## Validation and Testing
507
508
### where, whereEq
509
Test object properties against specifications.
510
511
```javascript { .api }
512
/**
513
* @param {Object} spec - Object mapping keys to predicate functions
514
* @param {Object} testObj - Object to test
515
* @returns {Boolean} True if all predicates pass
516
*/
517
R.where(spec, testObj)
518
519
const spec = {
520
name: R.is(String),
521
age: R.both(R.is(Number), R.gte(R.__, 18)),
522
active: R.equals(true)
523
};
524
525
const user1 = { name: 'John', age: 25, active: true };
526
const user2 = { name: 'Jane', age: 16, active: true };
527
528
R.where(spec, user1); // => true
529
R.where(spec, user2); // => false (age < 18)
530
531
// whereEq tests for exact value equality
532
const activeUser = R.whereEq({ active: true, status: 'online' });
533
activeUser({ name: 'John', active: true, status: 'online' }); // => true
534
```
535
536
### eqProps
537
Compare property values between objects.
538
539
```javascript { .api }
540
/**
541
* @param {String} key - Property key to compare
542
* @param {Object} obj1 - First object
543
* @param {Object} obj2 - Second object
544
* @returns {Boolean} True if property values are equal
545
*/
546
R.eqProps(key, obj1, obj2)
547
548
const user1 = { name: 'John', age: 30 };
549
const user2 = { name: 'John', age: 25 };
550
551
R.eqProps('name', user1, user2); // => true
552
R.eqProps('age', user1, user2); // => false
553
```
554
555
## Advanced Object Operations
556
557
### lens, lensProp, lensPath
558
Create lenses for focused object updates.
559
560
```javascript { .api }
561
// Property lens
562
const nameLens = R.lensProp('name');
563
const user = { name: 'john', age: 30 };
564
565
R.view(nameLens, user); // => 'john' (get)
566
R.set(nameLens, 'Jane', user); // => { name: 'Jane', age: 30 } (set)
567
R.over(nameLens, R.toUpper, user); // => { name: 'JOHN', age: 30 } (transform)
568
569
// Path lens for nested access
570
const profileNameLens = R.lensPath(['profile', 'name']);
571
const state = { profile: { name: 'john', age: 30 } };
572
573
R.over(profileNameLens, R.toUpper, state);
574
// => { profile: { name: 'JOHN', age: 30 } }
575
```
576
577
### invert, invertObj
578
Swap keys and values.
579
580
```javascript { .api }
581
const roles = { admin: 'Alice', user: 'Bob', guest: 'Charlie' };
582
583
// Simple inversion (last value wins for duplicates)
584
R.invertObj(roles); // => { Alice: 'admin', Bob: 'user', Charlie: 'guest' }
585
586
// Handle duplicate values with arrays
587
R.invert({ a: 'x', b: 'y', c: 'x' }); // => { x: ['a', 'c'], y: ['b'] }
588
```
589
590
These object functions provide a comprehensive toolkit for immutable object manipulation, enabling sophisticated data transformations while maintaining the functional programming paradigm.