Как стать автором
Обновить
536.18
OTUS
Цифровые навыки от ведущих экспертов

Ramda.js — библиотека, которая избавит вас от reduce и map-каши

Уровень сложностиПростой
Время на прочтение3 мин
Количество просмотров4.5K

Привет, Хабр!

Если вас когда‑либо раздражало, что 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 }
]

Задача:

  1. Выбрать только активных пользователей.

  2. Привести их имена к нормальному виду.

  3. Вернуть массив имён.

С 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. Записаться

Теги:
Хабы:
Всего голосов 11: ↑3 и ↓8-4
Комментарии16

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS