A practical functional library for JavaScript programmers.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
Get a property value from an object.
/**
* @param {String|Number} key - Property key to access
* @param {Object} obj - Object to read from
* @returns {*} Property value or undefined
*/
R.prop(key, obj)
const user = { name: 'John', age: 30, active: true };
R.prop('name', user); // => 'John'
R.prop('age', user); // => 30
R.prop('missing', user); // => undefined
// Curried usage for data extraction
const getName = R.prop('name');
const users = [{name: 'Alice'}, {name: 'Bob'}];
R.map(getName, users); // => ['Alice', 'Bob']Get multiple property values as an array.
/**
* @param {Array} keys - Array of property keys
* @param {Object} obj - Object to read from
* @returns {Array} Array of property values
*/
R.props(keys, obj)
const user = { name: 'John', age: 30, city: 'NYC' };
R.props(['name', 'age'], user); // => ['John', 30]
R.props(['city', 'name'], user); // => ['NYC', 'John']
R.props(['missing', 'name'], user); // => [undefined, 'John']
// Extract specific fields from objects
const extractInfo = R.props(['id', 'name', 'email']);
const users = [{id: 1, name: 'Alice', email: 'alice@example.com', extra: 'data'}];
R.map(extractInfo, users); // => [[1, 'Alice', 'alice@example.com']]Access nested properties safely.
/**
* @param {Array} pathArray - Array of keys for nested access
* @param {Object} obj - Object to traverse
* @returns {*} Nested property value or undefined
*/
R.path(pathArray, obj)
const user = {
profile: {
personal: { name: 'John', age: 30 },
contact: { email: 'john@example.com' }
}
};
R.path(['profile', 'personal', 'name'], user); // => 'John'
R.path(['profile', 'contact', 'email'], user); // => 'john@example.com'
R.path(['profile', 'missing', 'field'], user); // => undefined
R.path(['a', 'b'], { a: { b: 2 } }); // => 2
// Safe access prevents errors
R.path(['deep', 'nested', 'prop'], {}); // => undefined (no error)Access nested properties with default value.
/**
* @param {*} defaultValue - Value to return if path doesn't exist
* @param {Array} pathArray - Array of keys for nested access
* @param {Object} obj - Object to traverse
* @returns {*} Nested property value or default
*/
R.pathOr(defaultValue, pathArray, obj)
const config = { api: { timeout: 5000 } };
R.pathOr(3000, ['api', 'timeout'], config); // => 5000
R.pathOr(3000, ['api', 'retries'], config); // => 3000 (default)
R.pathOr('Unknown', ['user', 'name'], {}); // => 'Unknown'Create a deep copy of an object or array.
/**
* @param {*} value - Value to clone (object, array, or primitive)
* @returns {*} Deep copy of the value
*/
R.clone(value)
const original = {
name: 'John',
scores: [85, 90, 78],
profile: { age: 30, active: true }
};
const copied = R.clone(original);
copied.scores.push(95); // Original scores unchanged
copied.profile.age = 31; // Original profile unchanged
// Safe for nested structures
const nestedArray = [[1, 2], [3, 4]];
const clonedArray = R.clone(nestedArray);
clonedArray[0].push(3); // Original array unchanged
// Handles various data types
R.clone([1, 2, 3]); // => [1, 2, 3] (new array)
R.clone({a: 1}); // => {a: 1} (new object)
R.clone('hello'); // => 'hello' (primitives return as-is)Set a property (immutable).
/**
* @param {String|Number} key - Property key to set
* @param {*} value - Value to assign
* @param {Object} obj - Object to modify
* @returns {Object} New object with property set
*/
R.assoc(key, value, obj)
const user = { name: 'John', age: 30 };
R.assoc('age', 31, user); // => { name: 'John', age: 31 }
R.assoc('city', 'NYC', user); // => { name: 'John', age: 30, city: 'NYC' }
// Original object unchanged
console.log(user); // => { name: 'John', age: 30 }
// Curried usage for object updates
const addTimestamp = R.assoc('timestamp', Date.now());
const withTimestamp = addTimestamp({ data: 'value' });Set nested property (immutable).
/**
* @param {Array} pathArray - Array of keys for nested access
* @param {*} value - Value to set at path
* @param {Object} obj - Object to modify
* @returns {Object} New object with nested property set
*/
R.assocPath(pathArray, value, obj)
const user = { profile: { name: 'John', settings: { theme: 'dark' } } };
R.assocPath(['profile', 'name'], 'Jane', user);
// => { profile: { name: 'Jane', settings: { theme: 'dark' } } }
R.assocPath(['profile', 'settings', 'theme'], 'light', user);
// => { profile: { name: 'John', settings: { theme: 'light' } } }
// Create missing intermediate objects
R.assocPath(['new', 'nested', 'prop'], 'value', {});
// => { new: { nested: { prop: 'value' } } }Remove a property (immutable).
/**
* @param {String} key - Property key to remove
* @param {Object} obj - Object to modify
* @returns {Object} New object without the property
*/
R.dissoc(key, obj)
const user = { name: 'John', age: 30, temp: 'remove-me' };
R.dissoc('temp', user); // => { name: 'John', age: 30 }
R.dissoc('missing', user); // => { name: 'John', age: 30, temp: 'remove-me' }
// Remove multiple properties
const removeFields = R.pipe(
R.dissoc('temp'),
R.dissoc('internal')
);Remove nested property (immutable).
/**
* @param {Array} pathArray - Array of keys for nested access
* @param {Object} obj - Object to modify
* @returns {Object} New object without the nested property
*/
R.dissocPath(pathArray, obj)
const user = {
profile: {
name: 'John',
settings: { theme: 'dark', lang: 'en' }
}
};
R.dissocPath(['profile', 'settings', 'theme'], user);
// => { profile: { name: 'John', settings: { lang: 'en' } } }Transform object properties using functions.
/**
* @param {Object} transformations - Object mapping keys to transformation functions
* @param {Object} obj - Object to transform
* @returns {Object} New object with properties transformed
*/
R.evolve(transformations, obj)
const user = { name: ' john ', age: '30', active: 'true' };
const transformations = {
name: R.pipe(R.trim, R.toUpper),
age: parseInt,
active: R.equals('true')
};
R.evolve(transformations, user);
// => { name: 'JOHN', age: 30, active: true }
// Nested transformations
const nestedTransform = {
profile: {
name: R.toUpper,
age: R.add(1)
}
};
R.evolve(nestedTransform, {
profile: { name: 'john', age: 30 },
other: 'unchanged'
});
// => { profile: { name: 'JOHN', age: 31 }, other: 'unchanged' }Transform a specific property.
/**
* @param {String|Number} key - Property key to modify
* @param {Function} fn - Transformation function
* @param {Object} obj - Object to modify
* @returns {Object} New object with property transformed
*/
R.modify(key, fn, obj)
const user = { name: 'john', age: 30, scores: [85, 90, 78] };
R.modify('name', R.toUpper, user); // => { name: 'JOHN', age: 30, scores: [85, 90, 78] }
R.modify('age', R.add(1), user); // => { name: 'john', age: 31, scores: [85, 90, 78] }
R.modify('scores', R.append(95), user); // => { name: 'john', age: 30, scores: [85, 90, 78, 95] }Transform a nested property.
/**
* @param {Array} pathArray - Array of keys for nested access
* @param {Function} fn - Transformation function
* @param {Object} obj - Object to modify
* @returns {Object} New object with nested property transformed
*/
R.modifyPath(pathArray, fn, obj)
const state = {
user: {
profile: { name: 'john', visits: 5 }
}
};
R.modifyPath(['user', 'profile', 'name'], R.toUpper, state);
// => { user: { profile: { name: 'JOHN', visits: 5 } } }
R.modifyPath(['user', 'profile', 'visits'], R.inc, state);
// => { user: { profile: { name: 'john', visits: 6 } } }Shallow merge two objects.
/**
* @param {Object} obj1 - First object
* @param {Object} obj2 - Second object (properties override obj1)
* @returns {Object} New merged object
*/
R.merge(obj1, obj2)
const defaults = { timeout: 5000, retries: 3 };
const config = { timeout: 8000, debug: true };
R.merge(defaults, config);
// => { timeout: 8000, retries: 3, debug: true }
// Curried usage for applying defaults
const withDefaults = R.merge({ theme: 'light', lang: 'en' });
withDefaults({ theme: 'dark' }); // => { theme: 'dark', lang: 'en' }Deep merge objects with conflict resolution.
const obj1 = {
a: 1,
nested: { x: 10, y: 20 }
};
const obj2 = {
b: 2,
nested: { x: 30, z: 40 }
};
// Left object takes precedence
R.mergeDeepLeft(obj1, obj2);
// => { a: 1, b: 2, nested: { x: 10, y: 20, z: 40 } }
// Right object takes precedence
R.mergeDeepRight(obj1, obj2);
// => { a: 1, b: 2, nested: { x: 30, y: 20, z: 40 } }Merge objects with custom conflict resolution.
/**
* @param {Function} conflictFn - Function to resolve conflicts (leftVal, rightVal) -> resolvedVal
* @param {Object} obj1 - First object
* @param {Object} obj2 - Second object
* @returns {Object} New merged object
*/
R.mergeWith(conflictFn, obj1, obj2)
const obj1 = { a: 1, b: [1, 2], c: 'hello' };
const obj2 = { a: 2, b: [3, 4], d: 'world' };
// Concatenate conflicting arrays, add numbers, take right for others
R.mergeWith(
(left, right) => {
if (Array.isArray(left)) return R.concat(left, right);
if (typeof left === 'number') return left + right;
return right;
},
obj1,
obj2
);
// => { a: 3, b: [1, 2, 3, 4], c: 'hello', d: 'world' }Get object keys or values.
const obj = { name: 'John', age: 30, active: true };
R.keys(obj); // => ['name', 'age', 'active']
R.values(obj); // => ['John', 30, true]
// keysIn and valuesIn include inherited properties
function Person(name) { this.name = name; }
Person.prototype.species = 'human';
const john = new Person('John');
R.keys(john); // => ['name']
R.keysIn(john); // => ['name', 'species']Convert object to key-value pairs.
/**
* @param {Object} obj - Object to convert
* @returns {Array} Array of [key, value] pairs
*/
R.toPairs(obj)
const obj = { name: 'John', age: 30 };
R.toPairs(obj); // => [['name', 'John'], ['age', 30]]
// Convert back to object
R.fromPairs([['name', 'John'], ['age', 30]]); // => { name: 'John', age: 30 }Check property existence.
const obj = { name: 'John', profile: { age: 30 } };
// Own properties only
R.has('name', obj); // => true
R.has('toString', obj); // => false
// Including inherited properties
R.hasIn('toString', obj); // => true
// Nested property existence
R.hasPath(['profile', 'age'], obj); // => true
R.hasPath(['profile', 'email'], obj); // => falseSelect specific properties.
/**
* @param {Array} keys - Array of property keys to pick
* @param {Object} obj - Object to pick from
* @returns {Object} New object with only selected properties
*/
R.pick(keys, obj)
const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' };
R.pick(['id', 'name'], user); // => { id: 1, name: 'John' }
R.pick(['name', 'missing'], user); // => { name: 'John' }
// pickAll includes undefined for missing keys
R.pickAll(['name', 'missing'], user); // => { name: 'John', missing: undefined }
// Create safe user objects
const safeUser = R.pick(['id', 'name', 'email']);
safeUser(user); // => { id: 1, name: 'John', email: 'john@example.com' }Exclude specific properties.
/**
* @param {Array} keys - Array of property keys to omit
* @param {Object} obj - Object to omit from
* @returns {Object} New object without specified properties
*/
R.omit(keys, obj)
const user = { id: 1, name: 'John', password: 'secret', temp: 'data' };
R.omit(['password', 'temp'], user); // => { id: 1, name: 'John' }
// Remove sensitive data
const sanitize = R.omit(['password', 'apiKey', 'secret']);
sanitize(user); // => { id: 1, name: 'John' }Select properties by predicate.
/**
* @param {Function} predicate - Function to test values (value, key) -> Boolean
* @param {Object} obj - Object to filter
* @returns {Object} New object with properties that satisfy predicate
*/
R.pickBy(predicate, obj)
const data = { a: 1, b: 'hello', c: null, d: 42, e: undefined };
R.pickBy(R.is(Number), data); // => { a: 1, d: 42 }
R.pickBy(R.complement(R.isNil), data); // => { a: 1, b: 'hello', d: 42 }
R.pickBy((val, key) => key.length > 1, { a: 1, bb: 2, ccc: 3 }); // => { bb: 2, ccc: 3 }Test object properties against specifications.
/**
* @param {Object} spec - Object mapping keys to predicate functions
* @param {Object} testObj - Object to test
* @returns {Boolean} True if all predicates pass
*/
R.where(spec, testObj)
const spec = {
name: R.is(String),
age: R.both(R.is(Number), R.gte(R.__, 18)),
active: R.equals(true)
};
const user1 = { name: 'John', age: 25, active: true };
const user2 = { name: 'Jane', age: 16, active: true };
R.where(spec, user1); // => true
R.where(spec, user2); // => false (age < 18)
// whereEq tests for exact value equality
const activeUser = R.whereEq({ active: true, status: 'online' });
activeUser({ name: 'John', active: true, status: 'online' }); // => trueCompare property values between objects.
/**
* @param {String} key - Property key to compare
* @param {Object} obj1 - First object
* @param {Object} obj2 - Second object
* @returns {Boolean} True if property values are equal
*/
R.eqProps(key, obj1, obj2)
const user1 = { name: 'John', age: 30 };
const user2 = { name: 'John', age: 25 };
R.eqProps('name', user1, user2); // => true
R.eqProps('age', user1, user2); // => falseCreate lenses for focused object updates.
// Property lens
const nameLens = R.lensProp('name');
const user = { name: 'john', age: 30 };
R.view(nameLens, user); // => 'john' (get)
R.set(nameLens, 'Jane', user); // => { name: 'Jane', age: 30 } (set)
R.over(nameLens, R.toUpper, user); // => { name: 'JOHN', age: 30 } (transform)
// Path lens for nested access
const profileNameLens = R.lensPath(['profile', 'name']);
const state = { profile: { name: 'john', age: 30 } };
R.over(profileNameLens, R.toUpper, state);
// => { profile: { name: 'JOHN', age: 30 } }Swap keys and values.
const roles = { admin: 'Alice', user: 'Bob', guest: 'Charlie' };
// Simple inversion (last value wins for duplicates)
R.invertObj(roles); // => { Alice: 'admin', Bob: 'user', Charlie: 'guest' }
// Handle duplicate values with arrays
R.invert({ a: 'x', b: 'y', c: 'x' }); // => { x: ['a', 'c'], y: ['b'] }These object functions provide a comprehensive toolkit for immutable object manipulation, enabling sophisticated data transformations while maintaining the functional programming paradigm.
Install with Tessl CLI
npx tessl i tessl/npm-ramda