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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Если вас когда‑либо раздражало, что 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. Записаться

Источник: https://habr.com/ru/companies/otus/articles/886512/


Интересные статьи

Интересные статьи

Приветствую всех на Хабре. Я Сергей Михнов, CEO компании Пуск, интегратора Битрикс24. В свое время мы выбрали особый путь – специализацию на коробочной версии Битрикс24. В коммуникациях на сайте или п...
Доброго времени суток. При работе с Bitrix-инфраструктурой в определенный момент вы можете столкнуться с проблемой увеличения времени открытия различных страниц на вашей площадке (сайте), медленн...
Цель статьи, – показать примеры управления реализацией стратегии с помощью корпоративной единой информационной площадки на доступном инструменте, - Битрикс24. В статье на простом языке обсуждаются воз...
Что происходит, когда вы указываете в редакторе "font_size": 32? Я бы вам всё равно рассказал, но хорошо, что спросили. Попробуем догадаться. Я пользуюсь Sublime Text 4 под m...
В первой части нашего повествования мы показали как, имея на руках криптографический токен с поддержкой российской криптографии, создать запрос на получение сертификата, получить и установить сер...