A practical functional library for JavaScript programmers.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Ramda provides 13 essential functions for string manipulation and type checking. This includes 8 string functions for text processing and 5 type functions for runtime type validation and inspection.
// Convert to uppercase
R.toUpper('hello world'); // => 'HELLO WORLD'
R.toUpper('MiXeD cAsE'); // => 'MIXED CASE'
// Convert to lowercase
R.toLower('HELLO WORLD'); // => 'hello world'
R.toLower('MiXeD cAsE'); // => 'mixed case'
// Curried usage for transformations
const normalizeNames = R.map(R.pipe(
R.trim,
R.toLower,
R.replace(/\s+/g, ' ') // Normalize whitespace
));
normalizeNames([' ALICE ', 'bob', ' CAROL SMITH ']);
// => ['alice', 'bob', 'carol smith']
// Case-insensitive operations
const caseInsensitiveIncludes = R.pipe(
R.juxt([R.toLower, R.pipe(R.nthArg(1), R.toLower)]),
R.apply(R.includes)
);
const containsWord = R.curry((word, text) =>
R.pipe(R.toLower, R.includes(R.toLower(word)))(text)
);
containsWord('HELLO', 'hello world'); // => true// Remove whitespace from both ends
R.trim(' hello world '); // => 'hello world'
R.trim('\n\t text \n\t'); // => 'text'
R.trim('no-spaces'); // => 'no-spaces'
// Clean up user input
const sanitizeInput = R.pipe(
R.trim,
R.replace(/\s+/g, ' '), // Replace multiple spaces with single space
R.when(R.isEmpty, R.always('(empty)'))
);
sanitizeInput(' hello world '); // => 'hello world'
sanitizeInput(' '); // => '(empty)'
// Process form data
const cleanFormData = R.map(R.when(R.is(String), R.trim));
const formData = {
name: ' John Doe ',
email: ' john@example.com ',
age: 30
};
cleanFormData(formData);
// => { name: 'John Doe', email: 'john@example.com', age: 30 }// Split string by separator
R.split(' ', 'hello world foo'); // => ['hello', 'world', 'foo']
R.split(',', 'a,b,c,d'); // => ['a', 'b', 'c', 'd']
R.split('', 'hello'); // => ['h', 'e', 'l', 'l', 'o']
// Split by regex
R.split(/\s+/, 'hello world\ttest'); // => ['hello', 'world', 'test']
R.split(/[,;]/, 'a,b;c,d'); // => ['a', 'b', 'c', 'd']
// Parse CSV-like data
const parseCSVRow = R.pipe(
R.split(','),
R.map(R.trim),
R.reject(R.isEmpty)
);
parseCSVRow('name, age, city, '); // => ['name', 'age', 'city']
// Path manipulation
const getPathSegments = R.pipe(
R.split('/'),
R.reject(R.isEmpty)
);
getPathSegments('/users/123/profile'); // => ['users', '123', 'profile']
// Word processing
const getWords = R.pipe(
R.toLower,
R.split(/\W+/),
R.reject(R.isEmpty)
);
getWords('Hello, World! How are you?'); // => ['hello', 'world', 'how', 'are', 'you']// Replace substring or pattern
R.replace('foo', 'bar', 'foo foo foo'); // => 'bar foo foo' (first occurrence)
R.replace(/foo/g, 'bar', 'foo foo foo'); // => 'bar bar bar' (all occurrences)
// Template replacement
const template = 'Hello, {name}! Welcome to {site}.';
const replacePlaceholder = R.curry((key, value) =>
R.replace(new RegExp(`\\{${key}\\}`, 'g'), value)
);
const personalizeMessage = R.pipe(
replacePlaceholder('name', 'Alice'),
replacePlaceholder('site', 'Our Platform')
);
personalizeMessage(template); // => 'Hello, Alice! Welcome to Our Platform.'
// URL slug generation
const createSlug = R.pipe(
R.toLower,
R.trim,
R.replace(/[^\w\s-]/g, ''), // Remove special chars
R.replace(/\s+/g, '-'), // Replace spaces with hyphens
R.replace(/-+/g, '-'), // Replace multiple hyphens with single
R.replace(/^-|-$/g, '') // Remove leading/trailing hyphens
);
createSlug(' Hello, World! This is a Test '); // => 'hello-world-this-is-a-test'
// Clean phone numbers
const cleanPhone = R.pipe(
R.replace(/\D/g, ''), // Remove non-digits
R.replace(/^1/, ''), // Remove country code
R.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3') // Format
);
cleanPhone('1-555-123-4567'); // => '(555) 123-4567'// Test if string matches pattern
R.test(/^\d+$/, '12345'); // => true (all digits)
R.test(/^\d+$/, '123a5'); // => false
R.test(/^[a-z]+$/i, 'Hello'); // => true (letters only, case-insensitive)
// Extract matches from string
R.match(/\d+/g, 'abc 123 def 456'); // => ['123', '456']
R.match(/(\w+)@(\w+)/, 'user@domain.com'); // => ['user@domain', 'user', 'domain']
R.match(/xyz/, 'abc'); // => [] (no matches)
// Validation functions
const isEmail = R.test(/^[\w\.-]+@[\w\.-]+\.\w+$/);
const isPhone = R.test(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/);
const isZipCode = R.test(/^\d{5}(-\d{4})?$/);
isEmail('user@example.com'); // => true
isPhone('(555) 123-4567'); // => true
isZipCode('12345-6789'); // => true
// Extract information
const extractDomain = R.pipe(
R.match(/@([\w.-]+)/),
R.nth(1),
R.defaultTo('unknown')
);
extractDomain('user@example.com'); // => 'example.com'
extractDomain('invalid-email'); // => 'unknown'
// Parse structured data
const parseLogEntry = R.pipe(
R.match(/(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)/),
R.ifElse(
R.isEmpty,
R.always(null),
([, date, time, level, message]) => ({ date, time, level, message })
)
);
parseLogEntry('2023-01-15 14:30:45 [ERROR] Database connection failed');
// => { date: '2023-01-15', time: '14:30:45', level: 'ERROR', message: 'Database connection failed' }// Convert any value to string representation
R.toString(42); // => '42'
R.toString([1, 2, 3]); // => '[1, 2, 3]'
R.toString({a: 1, b: 2}); // => '{"a": 1, "b": 2}'
R.toString(new Date('2023-01-15')); // => 'new Date("2023-01-15T00:00:00.000Z")'
R.toString(null); // => 'null'
R.toString(undefined); // => 'undefined'
// Safe string conversion for display
const displayValue = R.pipe(
R.when(R.isNil, R.always('N/A')),
R.toString
);
displayValue(42); // => '42'
displayValue(null); // => 'N/A'
displayValue({id: 123}); // => '{"id": 123}'
// Format objects for logging
const formatForLog = R.pipe(
R.toString,
R.replace(/"/g, ''), // Remove quotes for cleaner output
R.replace(/,/g, ', ') // Add spacing after commas
);
formatForLog({user: 'alice', action: 'login'});
// => '{user: alice, action: login}'// Get type name as string
R.type(42); // => 'Number'
R.type('hello'); // => 'String'
R.type([1, 2, 3]); // => 'Array'
R.type({a: 1}); // => 'Object'
R.type(null); // => 'Null'
R.type(undefined); // => 'Undefined'
R.type(true); // => 'Boolean'
R.type(/regex/); // => 'RegExp'
R.type(() => {}); // => 'Function'
R.type(async () => {}); // => 'AsyncFunction'
R.type(new Date()); // => 'Date'
// Type-based processing
const processValue = R.cond([
[R.pipe(R.type, R.equals('Number')), R.multiply(2)],
[R.pipe(R.type, R.equals('String')), R.toUpper],
[R.pipe(R.type, R.equals('Array')), R.length],
[R.T, R.always('Unknown type')]
]);
processValue(5); // => 10
processValue('hello'); // => 'HELLO'
processValue([1, 2, 3]); // => 3
processValue(true); // => 'Unknown type'// Check if value is instance of constructor
R.is(Number, 42); // => true
R.is(String, 'hello'); // => true
R.is(Array, [1, 2, 3]); // => true
R.is(Object, {}); // => true
R.is(Date, new Date()); // => true
R.is(RegExp, /pattern/); // => true
// Check inheritance chain
R.is(Object, []); // => true (arrays inherit from Object)
R.is(Object, 'string'); // => false (primitives don't inherit)
// Custom constructor checking
function Person(name) { this.name = name; }
const john = new Person('John');
R.is(Person, john); // => true
R.is(Object, john); // => true
// Type guards for data validation
const isValidUser = R.where({
id: R.is(Number),
name: R.is(String),
email: R.is(String),
active: R.is(Boolean)
});
const user = { id: 123, name: 'Alice', email: 'alice@example.com', active: true };
isValidUser(user); // => true// Check for null or undefined
R.isNil(null); // => true
R.isNil(undefined); // => true
R.isNil(0); // => false
R.isNil(false); // => false
R.isNil(''); // => false
R.isNil([]); // => false
// Check for non-null and non-undefined
R.isNotNil(null); // => false
R.isNotNil(undefined); // => false
R.isNotNil(0); // => true
R.isNotNil(false); // => true
R.isNotNil(''); // => true
// Safe property access
const safeProp = R.curry((key, obj) =>
R.when(R.isNotNil, R.prop(key))(obj)
);
safeProp('name', {name: 'Alice'}); // => 'Alice'
safeProp('name', null); // => null
safeProp('name', undefined); // => undefined
// Filter out null/undefined values
const removeNils = R.filter(R.isNotNil);
removeNils([1, null, 2, undefined, 3]); // => [1, 2, 3]
// Provide defaults for nil values
const withDefaults = R.pipe(
R.when(R.isNil, R.always({})),
R.merge({name: 'Anonymous', age: 0})
);
withDefaults({name: 'Alice'}); // => {name: 'Alice', age: 0}
withDefaults(null); // => {name: 'Anonymous', age: 0}// Check type of object property
R.propIs(Number, 'age', {age: 30}); // => true
R.propIs(String, 'name', {name: 'Alice'}); // => true
R.propIs(Array, 'tags', {tags: ['a', 'b']}); // => true
R.propIs(Number, 'missing', {}); // => false
// Validate object structure
const validateProduct = R.where({
id: R.propIs(Number, 'id'),
name: R.propIs(String, 'name'),
price: R.propIs(Number, 'price'),
tags: R.propIs(Array, 'tags')
});
const product = {
id: 123,
name: 'Widget',
price: 29.99,
tags: ['electronics', 'gadget']
};
validateProduct(product); // => true
// Type-safe property extraction
const getNumberProp = R.curry((key, obj) =>
R.when(R.propIs(Number, key), R.prop(key))(obj)
);
getNumberProp('age', {age: 30}); // => 30
getNumberProp('age', {age: 'thirty'}); // => {age: 'thirty'} (unchanged)// Runtime type checking with detailed info
const analyzeValue = R.applySpec({
value: R.identity,
type: R.type,
isNil: R.isNil,
isNumber: R.is(Number),
isString: R.is(String),
isArray: R.is(Array),
isEmpty: R.isEmpty
});
analyzeValue('hello');
// => {
// value: 'hello',
// type: 'String',
// isNil: false,
// isNumber: false,
// isString: true,
// isArray: false,
// isEmpty: false
// }
// Type-based routing
const routeByType = R.cond([
[R.is(String), R.pipe(R.toUpper, R.concat('STRING: '))],
[R.is(Number), R.pipe(R.multiply(2), R.toString, R.concat('NUMBER: '))],
[R.is(Array), R.pipe(R.length, R.toString, R.concat('ARRAY LENGTH: '))],
[R.T, R.pipe(R.type, R.concat('UNKNOWN TYPE: '))]
]);
routeByType('hello'); // => 'STRING: HELLO'
routeByType(42); // => 'NUMBER: 84'
routeByType([1, 2, 3]); // => 'ARRAY LENGTH: 3'
routeByType(true); // => 'UNKNOWN TYPE: Boolean'These string and type functions provide essential utilities for text processing, data validation, and runtime type safety in functional JavaScript applications.
Install with Tessl CLI
npx tessl i tessl/npm-ramda