title: How to chain map and filter functions in the correct order
body: <p>I really like chaining <code>Array.prototype.map</code>,  <code>filter</code> and <code>reduce</code> to define a data transformation. Unfortunately, in a recent project that involved large log files, I could no longer get away with looping through my data multiple times...</p>
  
  <h2>My goal:</h2>
  
  <p>I want to create a function that chains <code>.filter</code> and <code>.map</code> methods by, instead of mapping over an array immediately, composing a function that loops over the data <em>once</em>. I.e.:</p>
  
  <pre><code>const DataTransformation = () =&gt; ({ 
      map: fn =&gt; (/* ... */), 
      filter: fn =&gt; (/* ... */), 
      run: arr =&gt; (/* ... */)
  });
  
  const someTransformation = DataTransformation()
      .map(x =&gt; x + 1)
      .filter(x =&gt; x &gt; 3)
      .map(x =&gt; 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]); 
  </code></pre>
  
  <h2>My attempt:</h2>
  
  <p>Inspired by <a href="https://stackoverflow.com/questions/44015014/mapping-an-object-to-array-of-objects-in-es6/44022167#44022167">this answer</a> and <a href="http://raganwald.com/2017/04/30/transducers.html" rel="noreferrer">this blogpost</a> I started writing a <code>Transduce</code> function.</p>
  
  <pre><code>const filterer = pred =&gt; reducer =&gt; (acc, x) =&gt;
      pred(x) ? reducer(acc, x) : acc;
  
  const mapper = map =&gt; reducer =&gt; (acc, x) =&gt;
      reducer(acc, map(x));
  
  const Transduce = (reducer = (acc, x) =&gt; (acc.push(x), acc)) =&gt; ({
      map: map =&gt; Transduce(mapper(map)(reducer)),
      filter: pred =&gt; Transduce(filterer(pred)(reducer)),
      run: arr =&gt; arr.reduce(reducer, [])
  });
  </code></pre>
  
  <h2>The problem:</h2>
  
  <p>The problem with the <code>Transduce</code> snippet above, is that it runs “backwards”... The last method I chain is the first to be executed:</p>
  
  <pre><code>const someTransformation = Transduce()
      .map(x =&gt; x + 1)
      .filter(x =&gt; x &gt; 3)
      .map(x =&gt; x / 2);
  
  // Instead of [ 2, 2.5 ] this returns []
  //  starts with (x / 2)       -&gt; [0.5, 1, 1.5, 2] 
  //  then filters (x &lt; 3)      -&gt; [] 
  const myData = someTransformation.run([ 1, 2, 3, 4]);
  </code></pre>
  
  <p>Or, in more abstract terms:</p>
  
  <blockquote>
    <p>Go from:</p>
  
  <pre><code>Transducer(concat).map(f).map(g) == (acc, x) =&gt; concat(acc, f(g(x)))
  </code></pre>
    
    <p>To:</p>
  
  <pre><code>Transducer(concat).map(f).map(g) == (acc, x) =&gt; concat(acc, g(f(x)))
  </code></pre>
    
    <p>Which is similar to:</p>
  
  <pre><code>mapper(f) (mapper(g) (concat))
  </code></pre>
  </blockquote>
  
  <p>I think I understand <em>why</em> it happens, but I can't figure out how to fix it without changing the “interface” of my function.</p>
  
  <h2>The question:</h2>
  
  <p>How can I make my <code>Transduce</code> method chain <code>filter</code> and <code>map</code> operations in the correct order? </p>
  
  <hr>
  
  <h3>Notes:</h3>
  
  <ul>
  <li>I'm only just learning about the naming of some of the things I'm trying to do. Please let me know if I've incorrectly used the <code>Transduce</code> term or if there are better ways to describe the problem.</li>
  <li>I'm aware I can do the same using a nested <code>for</code> loop:</li>
  </ul>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="true" data-babel="true">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>const push = (acc, x) =&gt; (acc.push(x), acc);
  const ActionChain = (actions = []) =&gt; {
    const run = arr =&gt;
      arr.reduce((acc, x) =&gt; {
        for (let i = 0, action; i &lt; 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 =&gt; fn =&gt; 
      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 =&gt;
      arr
        .map(x =&gt; x + 3)
        .filter(x =&gt; x % 3 === 0)
        .map(x =&gt; x / 3)
        .filter(x =&gt; x &lt; 40)
  };
  
  const actionChain = ActionChain()
    .map(x =&gt; x + 3)
    .filter(x =&gt; x % 3 === 0)
    .map(x =&gt; x / 3)
    .filter(x =&gt; x &lt; 40)
  
  
  const testData = Array.from(Array(100000), (x, i) =&gt; 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));</code></pre>
  </div>
  </div>
  </p>
  
  <ul>
  <li>Here's my attempt in a stack snippet:</li>
  </ul>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="true" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>const filterer = pred =&gt; reducer =&gt; (acc, x) =&gt;
    pred(x) ? reducer(acc, x) : acc;
  
  const mapper = map =&gt; reducer =&gt; (acc, x) =&gt; reducer(acc, map(x));
  
  const Transduce = (reducer = (acc, x) =&gt; (acc.push(x), acc)) =&gt; ({
    map: map =&gt; Transduce(mapper(map)(reducer)),
    filter: pred =&gt; Transduce(filterer(pred)(reducer)),
    run: arr =&gt; arr.reduce(reducer, [])
  });
  
  const sameDataTransformation = Transduce()
    .map(x =&gt; x + 5)
    .filter(x =&gt; x % 2 === 0)
    .map(x =&gt; x / 2)
    .filter(x =&gt; x &lt; 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]));</code></pre>
  </div>
  </div>
  </p>