Преобразователи здесь не имеют особого смысла.Ваша структура данных является рекурсивной.Лучший код для работы с рекурсивными структурами обычно требует рекурсивных алгоритмов.
Как работают преобразователи
(Роман Лютиков написал хорошее введение в преобразователи.)
Преобразователи предназначены для замены нескольких поездок через одни и те же данные одной, объединяя атомарные операции шагов в одну операцию.
Преобразователь был бы пригоден для преобразования этого кода:
xs.map(x => x * 7).map(x => x + 3).filter(isOdd(x)).take(5)
// ^ ^ ^ ^
// \ \ \ `------ Iteration 4
// \ \ `--------------------- Iteration 3
// \ `-------------------------------------- Iteration 2
// `----------------------------------------------------- Iteration 1
примерно так:
xs.reduce((r, x) => r.length >= 5 ? res : isOdd(x * 7 + 3) ? res.concat(x * 7 - 3) : res, [])
// ^
// `------------------------------------------------------- Just one iteration
В Ramda, поскольку map
, filter
и take
поддерживают преобразователь, мы можем преобразовать
const foo = pipe(
map(multiply(7)),
map(add(3)),
filter(isOdd),
take(3)
)
foo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [17, 31, 45]
(который повторяет данные четыре раза) в
const bar = compose(
map(multiply(7)),
map(add(3)),
filter(isOdd),
take(3)
)
into([], bar, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [17, 31, 45]
, который повторяет его только один раз.(Обратите внимание на переключение с pipe
на compose
. Преобразователи формируют в порядке, противоположном порядку простых функций.)
Обратите внимание, что ключевой момент таких преобразователей заключается в том, что они все работают одинаково.map
преобразует список в другой список, как и filter
и take
.Хотя у вас могут быть преобразователи, которые работают с разными типами, а map
и filter
могут также работать с такими типами полиморфно, они будут работать вместе, только если вы комбинируете функции, которые работают с одним и тем же типом.
Flatten
плохо подходит для преобразователей
Ваша структура более сложная.Хотя мы, безусловно, могли бы создать функцию, которая будет каким-либо образом сканировать ее (предзаказ, поступорядочение) и, таким образом, возможно, запустить конвейер преобразователя вместе с ней, логический способ справиться с рекурсивной структурой - это рекурсивный алгоритм.
Простой способ выровнять такую вложенную структуру выглядит примерно так:
const flatten = xs => xs.reduce(
(a, x) => concat(a, isArray(x) ? flatten(x) : [x]),
[]
);
(По разным техническим причинам код Рамды значительно сложнее.)
Эта рекурсивная версияТем не менее, он не очень подходит для работы с датчиками, которые по существу должны работать поэтапно.
Uniq
плохо подходит для датчиков
uniq
, с другойрука, имеет меньше смысла с такими преобразователями.Проблема в том, что контейнер, используемый uniq
, если вы хотите получить какую-либо выгоду от преобразователей, должен быть контейнером с быстрой вставкой и быстрым поиском, скорее всего, Set
или Object
.Допустим, мы используем Set
.Тогда у нас возникает проблема, поскольку наш flatten
работает со списками.
Другой подход
Поскольку мы не можем легко сложить существующие функции в одну, которая выполняет то, что вы ищете,нам, вероятно, нужно написать одноразовую запись.
Структура предыдущего решения позволяет довольно легко добавить ограничение уникальности.Опять же, это было:
const flatten = xs => xs.reduce(
(a, x) => concat(a, isArray(x) ? flatten(x) : [x]),
[]
);
С помощью вспомогательной функции для добавления всех элементов в Set
:
const addAll = (set, xs) => xs.reduce((s, x) => s.add(x), set)
Мы можем написать функцию, которая выравнивает, сохраняя только уникальные значения:
const flattenUniq = xs => xs.reduce(
(s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]),
new Set()
)
Обратите внимание, что это имеет структуру вышеупомянутого, переключаясь только на использование Set
и, следовательно, переключаясь с concat
на наши addAll
.
КонечноВы можете захотеть массив в конце.Мы можем сделать это, просто обернув это функцией Set -> Array
, например:
const flattenUniq = xs => Array.from(xs.reduce(
(s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]),
new Set()
))
Вы также можете рассмотреть возможность сохранения этого результата как Set
.Если вы действительно хотите получить коллекцию уникальных значений, логическим выбором будет Set
.
Такая функция не обладает элегантностью преобразованной функции без точек, но она работает, и открытая сантехника делаетотношения с исходной структурой данных и с простой функцией flatten
намного более ясны.
Я думаю, вы можете думать об этом длинном ответе как о длинном и скучном способе указать, что пользователь633183в комментариях говорится: «ни flatten, ни uniq не являются хорошими вариантами использования для преобразователей».