Привет, Хабр!
Если вас когда‑либо раздражало, что Array.prototype.map нельзя использовать для объектов или reduce постоянно требует передавать начальное значение, Ramda.js решает эти проблемы, делая код чище, декларативнее и удобнее.
Ramda — это библиотека для функционального программирования в JavaScript, которая по умолчанию не мутирует данные и поддерживает каррирование. В отличие от Lodash, где функциональность чаще заточена под удобство, Ramda больше ориентирована на чистоту кода и прогнозируемость работы функций.
Каррирование по умолчанию
Каррирование — это техника, при которой функция не требует все аргументы сразу, а возвращает новую функцию, ожидающую оставшиеся параметры.
Пример:
import * as R from 'ramda'; const add = R.add(10); // Создаём функцию, которая прибавляет 10 console.log(add(5)); // 15
Такой подход упрощает переиспользование кода и позволяет создавать частично применённые функции.
Композиция функций (pipe и compose)
Вместо длинных цепочек.map().filter().reduce(), где легко запутаться, в Ramda можно использовать функциональную композицию:
R.pipe (слева направо)
R.compose (справа налево)
Допустим, есть массив строк, где встречаются лишние пробелы и разный регистр:
const names = [' Alice ', ' BOB ', ' Eva', ' J'];
На чистом JS:
const normalizeNames = (names) => names .map(name => name.trim()) .map(name => name.toLowerCase()) .filter(name => name.length > 3); console.log(normalizeNames(names)); // ['alice', 'bob']
С Ramda:
const normalizeNames = R.pipe( R.map(R.trim), R.map(R.toLower), R.filter(name => name.length > 3) ); console.log(normalizeNames(names)); // ['alice', 'bob']
Стало компактнее и читабельнее.
Фильтрация, сортировка и маппинг в API
Предположим, есть API, возвращающее массив пользователей:
[ { "id": 1, "name": " Alice ", "age": 25, "active": true }, { "id": 2, "name": " Bob ", "age": 30, "active": false }, { "id": 3, "name": " Charlie ", "age": 35, "active": true } ]
Задача:
Выбрать только активных пользователей.
Привести их имена к нормальному виду.
Вернуть массив имён.
С Lodash:
import _ from 'lodash'; const processUsers = (users) => _.chain(users) .filter({ active: true }) .map(user => _.trim(user.name).toLowerCase()) .value();
С Ramda:
const processUsers = R.pipe( R.filter(R.propEq('active', true)), R.map(R.pipe(R.prop('name'), R.trim, R.toLower)) ); console.log(processUsers(users)); // ['alice', 'charlie']
Ramda избавляет от промежуточных переменных.
Работа с объектами: assoc, dissoc, evolve
В функциональном стиле нельзя мутировать объекты, но Ramda предлагает удобные методы для их изменения.
Добавление и удаление полей (assoc, dissoc)
const user = { id: 1, name: 'Alice', age: 25 }; // Добавить поле const updatedUser = R.assoc('status', 'active', user); console.log(updatedUser); // { id: 1, name: 'Alice', age: 25, status: 'active' } // Удалить поле const withoutAge = R.dissoc('age', updatedUser); console.log(withoutAge); // { id: 1, name: 'Alice', status: 'active' }
Изменение вложенных структур (evolve)
const user = { name: 'Alice', stats: { points: 10, level: 2 } }; const leveledUp = R.evolve({ stats: { points: R.add(5), level: R.inc } }, user); console.log(leveledUp); // { name: 'Alice', stats: { points: 15, level: 3 } }
Это удобно при обработке сложных JSON‑структур.
Группировка данных (groupBy)
Допустим, есть массив заказов, и нужно сгруппировать их по статусу.
const orders = [ { id: 1, status: 'pending', amount: 50 }, { id: 2, status: 'completed', amount: 150 }, { id: 3, status: 'pending', amount: 20 } ]; // Группировка по статусу const groupedOrders = R.groupBy(R.prop('status'), orders); console.log(groupedOrders); /* { pending: [ { id: 1, status: 'pending', amount: 50 }, { id: 3, status: 'pending', amount: 20 } ], completed: [ { id: 2, status: 'completed', amount: 150 } ] } */
Это полезно, например, при рендере таблиц в React.
Когда Ramda НЕ нужна?
Хотя Ramda мощна, не стоит использовать её везде. В простых случаях достаточно стандартных методов map, filter, reduce:
const users = [{ name: "Alice" }, { name: "Bob" }]; console.log(users.map(user => user.name));
Ramda полезна там, где:
Много операций над данными.
Нужно поддерживать иммутабельность.
Код должен быть декларативным и легко читаемым.
Используете Ramda? Делитесь комментариях!
А подробнее с Ramda можно ознакомиться здесь.
Будущим разработчикам на JS рекомендую посетить открытые уроки, которые совсем скоро проведут практикующие преподаватели Otus:
5 марта: Быстрый старт в Fullstack-разработку на наглядном примере с API и JavaScript. Записаться
19 марта: Прототипное наследование в JavaScript. Записаться
