Хабрахабр

[Перевод] Мышление в стиле Ramda: Неизменяемость и массивы

Первые шаги
2. 1. Частичное применение (каррирование)
4. Сочетаем функции
3. Бесточечная нотация
6. Декларативное программирование
5. Неизменяемость и массивы
8. Неизменяемость и объекты
7. Заключение
10. Линзы
9. Функциональные компоненты с React stateless функциями и Ramda
12. Использование Ramda с Redux
11. Модульные редюсеры и селекторы

Данный пост — это седьмая часть серии статей о функциональном программировании под названием "Мышление в стиле Ramda".

В шестой части мы говорили о работе с объектами JavaScript в функциональном и иммутабельном стиле.

В данном посте мы поговорим о подобной работе с массивами.

Чтение элементов массива

Ramda имеет ещё больше методов для чтения элементов массивов. В шестой части мы узнали о различных функциях Ramda, предназначенных для чтения свойств объектов, таких как prop, pick и has.

Давайте взглянем на них. Эквивалент prop для массива — это nth; эквивалент для pick — это slice, и эквивалент для has — это contains.

const numbers = [10, 20, 30, 40, 50, 60] nth(3, numbers) // => 40 (индексы с нуля) nth(-2, numbers) // => 50 (отрицательные числа стартуют с конца массива) slice(2, 5, numbers) // => [30, 40, 50] (см. ниже) contains(20, numbers) // => true

Slice берёт два индекса и возвращает подмассив, который начинается на первом индексе (начиная с нуля) и включает все элементы до второго индекса, но не включая элемент этого индекса.

Она также предоставляет функции для получения всех элементов, кроме первого (tail), всех, кроме последнего (init), первых N элементов (take(N)), и последних N элементов (takeLast(N)). Получение доступа к первому и последнему элементам массива довольно распространено, так что Ramda предоставляет короткие функции для этих случаев, head и last. Давайте взглянем на них в действии.

const numbers = [10, 20, 30, 40, 50, 60] head(numbers) // => 10
tail(numbers) // => [20, 30, 40, 50, 60] last(numbers) // => 60
init(numbers) // => [10, 20, 30, 40, 50] take(3, numbers) // => [10, 20, 30]
takeLast(3, numbers) // => [40, 50, 60]

Добавляем, обновляем и удаляем элементы массива

Изучая работу с объектами, мы узнали о функциях assoc, dissoc и omit для добавления, обновления и удаления свойств.

Наиболее распространённые — это insert и update, но Ramda также предоставляет методы append и prepend для типичных случаев добавления элементов в начало и конец массива. Так как массивы имеют упорядоченную структуру данных, у нас есть несколько методов, которые выполняют ту же работу, что и assoc для объектов. insert, append, и prepend добавляют новые элементы в массив; update "заменяет" определённый элемент в массиве новым значением.

Как вы можете ожидать от функциональной библиотеки, все эти функции возвращают новый массив с ожидаемыми изменениями; оригинальный же массив никогда не изменяется.

const numbers = [10, 20, 30, 40, 50, 60] insert(3, 35, numbers) // => [10, 20, 30, 35, 40, 50, 60] append(70, numbers) // => [10, 20, 30, 40, 50, 60, 70] prepend(0, numbers) // => [0, 10, 20, 30, 40, 50, 60] update(1, 15, numbers) // => [10, 15, 30, 40, 50, 60]

Ramda также предоставляет метод concat для выполнения той же операции с массивами. Для объединения двух объектов в один, мы ранее узнали о методе merge.

const numbers = [10, 20, 30, 40, 50, 60] concat(numbers, [70, 80, 90]) // => [10, 20, 30, 40, 50, 60, 70, 80, 90]

Это выглядит логичным при использовании этого метода в отдельности от другого кода, но, также как и с merge, эта логика может привести не совсем к тому, что мы хотели бы ожидать, если мы будем использовать этот метод в нашем конвеере. Обратите внимание, что второй массив присоединился к первому. Я нашёл полезным для себя написание функции-помощника, concatAfter: const concatAfter = flip(concat), чтобы использовать её в своих конвеерах.

remove удаляет элементы по их индексу, в то время как without удаляет их по их значению. Ramda также предоставляет несколько возможностей для удаления элементов. Также есть такие методы как drop и dropLast для типичных случаев, когда мы удаляем элементы из начала или конца массива.

const numbers = [10, 20, 30, 40, 50, 60] remove(2, 3, numbers) // => [10, 20, 60] without([30, 40, 50], numbers) // => [10, 20, 60] drop(3, numbers) // => [40, 50, 60] dropLast(3, numbers) // => [10, 20, 30]

Эта неконсистентность может сбить с толку, если вы не будете знать об этом. Обратите внимание, что remove принимает индекс и количество, в то время как slice принимает два индекса.

Преобразование элементов

Также как и с объектами, мы можем пожелать обновить элемент массива, применив функцию к оригинальному значению.

const numbers = [10, 20, 30, 40, 50, 60] // умножим третий элемент массива на 10
update(2, multiply(10, nth(2, numbers)), numbers) // => [10, 20, 300, 40, 50, 60]

Но в отличии от evolve, adjust работает только с одним элементом массива. Чтобу упростить этот типичный случай, Ramda предоставляет метод adjust, который работает очень похожим образом на evolve для объектов.

const numbers = [10, 20, 30, 40, 50, 60] // умножим третий элемент массива на 10
adjust(multiply(10), 2, numbers)

Это может быть источником ошибок, но оно имеет смысл тогда, когда вы будете рассматривать частичное применение. Обратите внимание, что первые два аргумента к adjust идут наоборот, если сравнивать их с update. Вы можете пожелать сделать для себя функцию изменения adjust(multiply(10)) и впоследствии решить, какой именно индекс массива изменять с помощью неё.

Заключение

Это позволяет нам строить программы, состоящие из маленьких, функциональных строительных блоков, совмещая функции, которые будут делать то, что нам нужно, и всё это без изменения всех наших структур данных. Теперь у нас есть инструменты для работы с массивами и объектами в декларативном и иммутабельном стиле.

Далее

Ramda предоставляет и другие основные инструменты для выполнения данных операций, линзы. Мы изучили способы чтения, обновления и трансформации свойств объектов и элементов массивов. Следующая статья о линзах должна будет показать нам, как они работают.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть