Plain JS vs Immutable vs mori vs underscore vs lodash vs Ramda (v23)

Revision 23 of this benchmark created by PhiLho on


Description

Testing complex, near real world data manipulation of a non-trivial dataset, with plain JavaScript (v.3, old school for / for in loops), v.5, more functional, another v.5 version using reduce()), Immutable.js, Mori.js, Underscore.js and its rival LoDash.js, and the new kid in the block, Ramda.js.

Preparation HTML

<script src="http://s3-eu-west-1.amazonaws.com/kosstest/commits.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/immutable/3.6.2/immutable.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mori/0.3.2/mori.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<script>
var _l = _.noConflict();
</script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
<script>
var _u = _.noConflict();
</script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min.js"></script>

Setup

window.immutableCommits = Immutable.fromJS(commitsOrigin);
    window.moriCommits = mori.toClj(commitsOrigin);

Test runner

Ready to run.

Testing in
TestOps/sec
Plain JS v5
// Using forEach and Object.keys from JavaScript 5

// 1. Group by authors
var groupedByAuthors = {};
commitsOrigin.forEach(function (c) 
{
  var email = c.commit.author.email;
  (groupedByAuthors[email] || (groupedByAuthors[email] = [])).push(c);
});

// 2. Sort by authors (asc)
var sortedAuthors = Object.keys(groupedByAuthors).sort();
var result = {};
sortedAuthors.forEach(function (author) 
{
  // 3. Group by hours
  var groupedByHours = {};
  groupedByAuthors[author].forEach(function (c) 
  {
    var date = new Date(c.commit.author.date);
    date.setMinutes(0);
    var hour = date.toISOString();
    (groupedByHours[hour] || (groupedByHours[hour] = [])).push(c);
  });

  // 4. Sort by hours (desc)
  var sortedHours = Object.keys(groupedByHours).sort().reverse();
  var groupedAndSortedByHours = {};
  sortedHours.forEach(function (hour) 
  {
    var commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    var sortedByTime = commits.sort(function (a, b) 
    {
      return a.commit.author.date > b.commit.author.date ? -1 : 1;
    });

    groupedAndSortedByHours[hour] = sortedByTime;
  });

  result[author] = groupedAndSortedByHours;
});
 
ready
Immutable
var commits = immutableCommits;

// 1. Group by authors
var groupedByAuthors = commits.groupBy(function(c) {
  return c.getIn(['commit', 'author', 'email']);
});

// 2. Sort by authors (asc)
var sortedAuthors = groupedByAuthors.keySeq().sort();
var result = sortedAuthors.reduce(function(acc, author) {

  // 3. Group by hours
  var groupedByHours = groupedByAuthors.get(author).groupBy(function(c) {
    var date = new Date(c.getIn(['commit', 'author', 'date']));
    date.setMinutes(0);
    return date.toISOString();
  });

  // 4. Sort by hours (desc)
  var sortedHours = groupedByHours.keySeq().sort().reverse();
  var groupedAndSortedByHours = sortedHours.reduce(function(acc, hour) {
    var commits = groupedByHours.get(hour);

    // 5. Sort by time (desc)
    var sortedByTime = commits.sortBy(function(c) {
      return c.getIn(['commit', 'author', 'date']);
    }).reverse();

    return acc.set(hour, sortedByTime);
  }, Immutable.OrderedMap());

  return acc.set(author, groupedAndSortedByHours);
}, Immutable.OrderedMap());

var resultInJS = result.toJS();
ready
mori
// 1. Group by authors
var groupedByAuthors = mori.groupBy(function(c) {
  return mori.getIn(c, ['commit', 'author', 'email']);
}, moriCommits);

// 2. Sort by authors (asc)
var sortedAuthors = mori.sort(mori.keys(groupedByAuthors));
var result = mori.reduce(function(acc, author) {

  // 3. Group by hours
  var groupedByHours = mori.groupBy(function(c) {
    var date = new Date(mori.getIn(c, ['commit', 'author', 'date']));
    date.setMinutes(0);
    return date.toISOString();
  }, mori.get(groupedByAuthors, author));

  // 4. Sort by hours (desc)

  var sortedHours = mori.reverse(mori.sort(mori.keys(groupedByHours)));
  var groupedAndSortedByHours = mori.reduce(function(acc, hour) {
    var commits = mori.get(groupedByHours, hour);

    // 5. Sort by time (desc)
    var sortedByTime = mori.reverse(mori.sortBy(function(c) {
      return mori.getIn(c, ['commit', 'author', 'date']);
    }, commits));

    return mori.conj(acc, mori.vector(hour, sortedByTime))
  }, mori.vector(), sortedHours);

  return mori.conj(acc, mori.vector(author, groupedAndSortedByHours));
}, mori.vector(), sortedAuthors);

var resultInJS = mori.toJs(result);
ready
lodash
// 1. Group by authors
var groupedByAuthors = _l.groupBy(commitsOrigin, function (c)
{
  return c.commit.author.email;
});

// 2. Sort by authors (asc)
var sortedAuthors = _l.chain(groupedByAuthors).keys().sortBy().value();
var result = _l.reduce(sortedAuthors, function (authorAccumulator, author)
{
  // 3. Group by hours
  var groupedByHours = _l.groupBy(groupedByAuthors[author], function (c) 
  {
    var date = new Date(c.commit.author.date);
    date.setMinutes(0);
    return date.toISOString();
  });

  // 4. Sort by hours (desc)
  var sortedHours = _l.chain(groupedByHours).keys().sortBy().reverse().value();
  var groupedAndSortedByHours = _l.reduce(sortedHours, function (hourAccumulator, hour) 
  {
    var commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    var sortedByTime = _l.sortBy(commits, function (c) 
    {
      return c.commit.author.date;
    }).reverse();

    hourAccumulator[hour] = sortedByTime;
    return hourAccumulator;
  }, {});

  authorAccumulator[author] = groupedAndSortedByHours;
  return authorAccumulator;
}, {});
 
ready
underscore
// 1. Group by authors
var groupedByAuthors = _u.groupBy(commitsOrigin, function (c)
{
  return c.commit.author.email;
});

// 2. Sort by authors (asc)
var sortedAuthors = _u.chain(groupedByAuthors).keys().sortBy().value();
var result = _u.reduce(sortedAuthors, function (authorAccumulator, author)
{
  // 3. Group by hours
  var groupedByHours = _u.groupBy(groupedByAuthors[author], function (c) 
  {
    var date = new Date(c.commit.author.date);
    date.setMinutes(0);
    return date.toISOString();
  });

  // 4. Sort by hours (desc)
  var sortedHours = _u.chain(groupedByHours).keys().sortBy().reverse().value();
  var groupedAndSortedByHours = _u.reduce(sortedHours, function (hourAccumulator, hour) 
  {
    var commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    var sortedByTime = _u.sortBy(commits, function (c) 
    {
      return c.commit.author.date;
    }).reverse();

    hourAccumulator[hour] = sortedByTime;
    return hourAccumulator;
  }, {});

  authorAccumulator[author] = groupedAndSortedByHours;
  return authorAccumulator;
}, {});
 
ready
Plain JS v3
// Using old-school JS 3 for / for in loops

var i, j, k, lenI, lenJ, lenK, c;
var email, author, hour, date;

// 1. Group by authors
var groupedByAuthors = {};
for (i = 0, lenI = commitsOrigin.length; i < lenI; i++)
{
  c = commitsOrigin[i];
  email = c.commit.author.email;
  (groupedByAuthors[email] || (groupedByAuthors[email] = [])).push(c);
}

// 2. Sort by authors (asc)
var sortedAuthors = [];
for (author in groupedByAuthors) 
{
  if (groupedByAuthors.hasOwnProperty(author)) 
  {
    sortedAuthors.push(author);
  }
}
sortedAuthors.sort();

var result = {};
for (i = 0, lenI = sortedAuthors.length; i < lenI; i++)
{
  var author = sortedAuthors[i];

  // 3. Group by hours
  var groupedByHours = {};
  var commits = groupedByAuthors[author];
  for (j = 0, lenJ = commits.length; j < lenJ; j++)
  {
    c = commits[j];
    date = new Date(c.commit.author.date);
    date.setMinutes(0);
    hour = date.toISOString();
    (groupedByHours[hour] || (groupedByHours[hour] = [])).push(c);
  }

  // 4. Sort by hours (desc)
  var sortedHours = [];
  for (hour in groupedByHours) 
  {
    if (groupedByHours.hasOwnProperty(hour)) 
    {
      sortedHours.push(hour);
    }
  }
  sortedHours.sort().reverse();

  var groupedAndSortedByHours = {}, groupedCommits, sortedByTime;
  for (j = 0, lenJ = sortedHours.length; j < lenJ; j++)
  {
    hour = sortedHours[j];
    commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    sortedByTime = commits.sort(function(a, b) 
    {
      return a.commit.author.date > b.commit.author.date ? -1 : 1;
    });

    groupedAndSortedByHours[hour] = sortedByTime;
  }

  result[author] = groupedAndSortedByHours;
}
 
ready
Ramda
// 1. Group by authors
var groupedByAuthors = R.groupBy(function (c)
{
  return c.commit.author.email;
}, commitsOrigin);

// 2. Sort by authors (asc)
var keySorter = R.pipe(R.keys(), R.sort(R._))
var sortedAuthors = keySorter(groupedByAuthors);
var result = R.reduce(function (authorAccumulator, author)
{
  // 3. Group by hours
  var groupedByHours = R.groupBy(function (c) 
  {
    var date = new Date(c.commit.author.date);
    date.setMinutes(0);
    return date.toISOString();
  }, groupedByAuthors[author]);

  // 4. Sort by hours (desc)
  var sortedHours = R.reverse(keySorter(groupedByHours));
  var groupedAndSortedByHours = R.reduce(function (hourAccumulator, hour) 
  {
    var commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    var sortedByTime = R.reverse(R.sort(function (c) 
    {
      return c.commit.author.date;
    }, commits));

    hourAccumulator[hour] = sortedByTime;
    return hourAccumulator;
  }, {}, sortedHours);

  authorAccumulator[author] = groupedAndSortedByHours;
  return authorAccumulator;
}, {}, sortedAuthors);
 
ready
Functional JS 5
// Like Plain JS v5, but also using reduce()

// 1. Group by authors
var groupedByAuthors = {};
commitsOrigin.forEach(function (c) 
{
  var email = c.commit.author.email;
  (groupedByAuthors[email] || (groupedByAuthors[email] = [])).push(c);
});

// 2. Sort by authors (asc)
var sortedAuthors = Object.keys(groupedByAuthors).sort();
var result = sortedAuthors.reduce(function (authorAccumulator, author) 
{
  // 3. Group by hours
  var groupedByHours = {};
  groupedByAuthors[author].forEach(function (c) 
  {
    var date = new Date(c.commit.author.date);
    date.setMinutes(0);
    var hour = date.toISOString();
    (groupedByHours[hour] || (groupedByHours[hour] = [])).push(c);
  });

  // 4. Sort by hours (desc)
  var sortedHours = Object.keys(groupedByHours).sort().reverse();
  var groupedAndSortedByHours = sortedHours.reduce(function (hourAccumulator, hour) 
  {
    var commits = groupedByHours[hour];

    // 5. Sort by time (desc)
    var sortedByTime = commits.sort(function (a, b) 
    {
      return a.commit.author.date > b.commit.author.date ? -1 : 1;
    });

    hourAccumulator[hour] = sortedByTime;
    return hourAccumulator;
  }, {});

  authorAccumulator[author] = groupedAndSortedByHours;
  return authorAccumulator;
}, {});
 
ready

Revisions

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