...

чень нравится цепочкаArray.prototype.map, filter а такжеreduce определить преобразование данных. К сожалению, в недавнем проекте, который включал большие файлы журналов, я больше не мог сойти с циклом через мои данные несколько раз ...

Моя цель:

Я хочу создать функцию, которая цепочки.filter а также.map методы, вместо того, чтобы немедленно отображать массив, составляя функцию, которая перебирает данныеодин раз, т.е .:

const DataTransformation = () => ({ 
    map: fn => (/* ... */), 
    filter: fn => (/* ... */), 
    run: arr => (/* ... */)
});

const someTransformation = DataTransformation()
    .map(x => x + 1)
    .filter(x => x > 3)
    .map(x => x / 2);

// returns [ 2, 2.5 ] without creating [ 2, 3, 4, 5] and [4, 5] in between
const myData = someTransformation.run([ 1, 2, 3, 4]); 
Моя попытка:

Вдохновленныйэтот ответ а такжеэтот пост Я начал писатьTransduce функция.

const filterer = pred => reducer => (acc, x) =>
    pred(x) ? reducer(acc, x) : acc;

const mapper = map => reducer => (acc, x) =>
    reducer(acc, map(x));

const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
    map: map => Transduce(mapper(map)(reducer)),
    filter: pred => Transduce(filterer(pred)(reducer)),
    run: arr => arr.reduce(reducer, [])
});
Эта проблема:

Проблема сTransduce фрагмент выше, это то, что он работает «задом наперед» ... последний метод, который я цепочка, выполняется первым:

const someTransformation = Transduce()
    .map(x => x + 1)
    .filter(x => x > 3)
    .map(x => x / 2);

// Instead of [ 2, 2.5 ] this returns []
//  starts with (x / 2)       -> [0.5, 1, 1.5, 2] 
//  then filters (x < 3)      -> [] 
const myData = someTransformation.run([ 1, 2, 3, 4]);

Или, в более абстрактных терминах:

Идти от:

Transducer(concat).map(f).map(g) == (acc, x) => concat(acc, f(g(x)))

Для того, чтобы:

Transducer(concat).map(f).map(g) == (acc, x) => concat(acc, g(f(x)))

Который похож на:

mapper(f) (mapper(g) (concat))

я думаю, что понялЗачем такое случается, но я не могу понять, как это исправить, не изменив «интерфейс» моей функции.

Вопрос:

Как я могу сделать мойTransduce цепочка методовfilter а такжеmap операции в правильном порядке?

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

const push = (acc, x) => (acc.push(x), acc);
const ActionChain = (actions = []) => {
  const run = arr =>
    arr.reduce((acc, x) => {
      for (let i = 0, action; i < actions.length; i += 1) {
        action = actions[i];

        if (action.type === "FILTER") {
          if (action.fn(x)) {
            continue;
          }

          return acc;
        } else if (action.type === "MAP") {
          x = action.fn(x);
        }
      }

      acc.push(x);
      return acc;
    }, []);

  const addAction = type => fn => 
    ActionChain(push(actions, { type, fn }));

  return {
    map: addAction("MAP"),
    filter: addAction("FILTER"),
    run
  };
};

// Compare to regular chain to check if 
// there's a performance gain
// Admittedly, in this example, it's quite small...
const naiveApproach = {
  run: arr =>
    arr
      .map(x => x + 3)
      .filter(x => x % 3 === 0)
      .map(x => x / 3)
      .filter(x => x < 40)
};

const actionChain = ActionChain()
  .map(x => x + 3)
  .filter(x => x % 3 === 0)
  .map(x => x / 3)
  .filter(x => x < 40)


const testData = Array.from(Array(100000), (x, i) => i);

console.time("naive");
const result1 = naiveApproach.run(testData);
console.timeEnd("naive");

console.time("chain");
const result2 = actionChain.run(testData);
console.timeEnd("chain");
console.log("equal:", JSON.stringify(result1) === JSON.stringify(result2));

Вот моя попытка в фрагменте стека:

const filterer = pred => reducer => (acc, x) =>
  pred(x) ? reducer(acc, x) : acc;

const mapper = map => reducer => (acc, x) => reducer(acc, map(x));

const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
  map: map => Transduce(mapper(map)(reducer)),
  filter: pred => Transduce(filterer(pred)(reducer)),
  run: arr => arr.reduce(reducer, [])
});

const sameDataTransformation = Transduce()
  .map(x => x + 5)
  .filter(x => x % 2 === 0)
  .map(x => x / 2)
  .filter(x => x < 4);
  
// It's backwards:
// [-1, 0, 1, 2, 3]
// [-0.5, 0, 0.5, 1, 1.5]
// [0]
// [5]
console.log(sameDataTransformation.run([-1, 0, 1, 2, 3, 4, 5]));

Ответы на вопрос(2)

Ваш ответ на вопрос