Chain vs Pipe (v6)

Revision 6 of this benchmark created by tomByrer on


Description

Provide pipe as an alternative to chain.

Pros:

  1. It's faster.
  2. It can be used to compose a function in chaining style.
  3. It accepts verbs (methods) that aren't built into underscore.
  4. It's implementation is smaller than chain's -- just 1 method.
  5. It's more succinct -- get more done with less effort.

Cons:

  1. Syntax is less familiar.

For what it's worth it employs pointfree style which is preferred for reasons of its own. http://www.haskell.org/haskellwiki/Pointfree

v6: updated libs

Preparation HTML

<script src='//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore.js'></script>
<script src='//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.underscore.js'></script>
<script src='//rawgithub.com/dtao/lazy.js/master/lazy.js'></script>
<script>
  var lodash = _.noConflict();

  function pipe(seed){
    var seeded = arguments.length > 0, composed = [];
    return function piping(verb){
      if (arguments.length === 0) {
        var fn = _.compose.apply(null, composed);
        return seeded ? fn(seed) : fn;
      }
      var args = _.rest(arguments);
      composed.unshift(function(obj){
        return verb.apply(null, [obj].concat(args));
      });        
      return piping;
    }
  };
  _.pipe = pipe;
</script>

Setup

var _ = window._,
        lodash = window.lodash,
        lyrics = [],
        index = 0;
    
    _.times(25, function() {
      lyrics.push(
        {line : index++, words : "I'm a lumberjack and I'm okay"},
        {line : index++, words : "I sleep all night and I work all day"},
        {line : index++, words : "He's a lumberjack and he's okay"},
        {line : index++, words : "He sleeps all night and he works all day"},
        {line : index++, words : "http://www.youtube.com/watch?v=mL7n5mEmXJo"}
      );
    });
    
    var chainCounter = function(collection) {
      return _(collection)
        .chain()
        .map(function(line) { return line.words.split(' '); })
        .flatten()
        .filter(function(word) { return !/[aio]/i.test(word); })
        .reduce(function(counts, word) {
          counts[word] = (counts[word] || 0) + 1;
          return counts;
        }, {})
       .value();
    };
    
    var lodashChainCounter = function(collection) {
      return lodash(collection)
        .chain()
        .map(function(line) { return line.words.split(' '); })
        .flatten()
        .filter(function(word) { return !/[aio]/i.test(word); })
        .reduce(function(counts, word) {
          counts[word] = (counts[word] || 0) + 1;
          return counts;
        }, {})
        .value();
    };
    
    var genericCounter = function(collection) {
      var result = _.map(collection, function(line) {
        return line.words.split(' ');
      });
      result = _.flatten(result);
      result = _.filter(result, function(word) {
        return !/[aio]/i.test(word);
      });
      return _.reduce(result, function(counts, word) {
        counts[word] = (counts[word] || 0) + 1;
        return counts;
      }, {});
    };
    
    var lodashGenericCounter = function(collection) {
      var result = lodash.map(collection, function(line) {
        return line.words.split(' ');
      });
      result = lodash.flatten(result);
      result = lodash.filter(result, function(word) {
        return !/[aio]/i.test(word);
      });
      return lodash.reduce(result, function(counts, word) {
        counts[word] = (counts[word] || 0) + 1;
        return counts;
      }, {});
    };
    
    var pipeCounter = _.pipe()
      (_.map, function(line) { return line.words.split(' '); })
      (_.flatten)
      (_.filter, function(word) { return !/[aio]/i.test(word); })
      (_.reduce, function(counts, word) {
          counts[word] = (counts[word] || 0) + 1;
          return counts;
        }, {})
      ();
    
    var lazyCounter = function(collection) {
      return Lazy(collection)
        .map(function(line) { return line.words.split(' '); })
        .flatten()
        .filter(function(word) { return !/[aio]/i.test(word); })
        .reduce(function(counts, word) {
          counts[word] = (counts[word] || 0) + 1;
          return counts;
        }, {});
    };

Test runner

Ready to run.

Testing in
TestOps/sec
Chain
chainCounter(lyrics);
ready
Pipe
pipeCounter(lyrics);
ready
Generic
genericCounter(lyrics);
ready
Lazy
lazyCounter(lyrics);
ready
Lo-Dash chain
lodashChainCounter(lyrics);
ready
Lo-Dash generic
lodashGenericCounter(lyrics);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.