Преобразователь сглаживает и уникален - PullRequest
0 голосов
/ 24 августа 2018

Мне интересно, есть ли способ использовать преобразователь для сглаживания списка и фильтрации по уникальным значениям?

С помощью цепочки это очень просто:

import {uniq, flattenDeep} from 'lodash';|

const arr = [1, 2, [2, 3], [1, [4, 5]]];

uniq(flattendDeep(arr)); // ->  [1, 2, 3, 4, 5]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.min.js"></script>

Но здесь мы дважды делаем цикл по списку (+ n по слою глубины).Не идеально.

Я пытаюсь добиться использования преобразователя для этого случая.Я прочитал документацию Ramda об этом https://ramdajs.com/docs/#transduce,, но все еще не могу найти способ написать это правильно.

В настоящее время я использую функцию Reduce с рекурсивной функцией внутри нее:

import {isArray} from 'lodash';

const arr = [1, 2, [2, 3], [1, [4, 5]]];

const flattenDeepUniq = (p, c) => {
    if (isArray(c)) {
        c.forEach(o => p = flattenDeepUniq(p, o));
    }
    else {
        p = !p.includes(c) ? [...p, c] : p;
    }

    return p;
};

arr.reduce(flattenDeepUniq, []) // -> [1, 2, 3, 4, 5]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.min.js"></script>

У нас есть один цикл над элементами (+ n петля с глубокими слоями глубины), который кажется лучше и более оптимизированным.

Это дажеМожно ли использовать преобразователь и итератор в этом случае?Для получения дополнительной информации о функции преобразования Ramda: https://gist.github.com/craigdallimore/8b5b9d9e445bfa1e383c569e458c3e26

1 Ответ

0 голосов
/ 24 августа 2018

Преобразователи здесь не имеют особого смысла.Ваша структура данных является рекурсивной.Лучший код для работы с рекурсивными структурами обычно требует рекурсивных алгоритмов.

Как работают преобразователи

(Роман Лютиков написал хорошее введение в преобразователи.)

Преобразователи предназначены для замены нескольких поездок через одни и те же данные одной, объединяя атомарные операции шагов в одну операцию.

Преобразователь был бы пригоден для преобразования этого кода:

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 не являются хорошими вариантами использования для преобразователей».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...