Function versus procedural aggregation (v2)

Revision 2 of this benchmark created on


Preparation HTML

<script src=" https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js "></script>

Setup

var data = [];

function randInt(lo, hi) {
	return Math.floor(Math.random()*(hi-lo+1))+lo;
}
function randDate() {
	const m = randInt(1,12)
	const d = randInt(1,30)
	return `2023-${m}-${d}`
}

function randAsset() {
	const assets = ['usd', 'btc', 'eth', 'mro', 'nya', 'dog'];
	return assets[randInt(0,assets.length-1)]
}
for(let i=0; i < 100000; i++) {
	data.push({
		date: randDate(),
		asset: randAsset(),
		foo: 'bar',
		rolling_amount: randInt(0,1000).toString(),
	});
}

Test runner

Ready to run.

Testing in
TestOps/sec
functional
const datedEntries = data.filter(e => !!e && !!e.date && !!e.asset)

const entriesBydate = Object.groupBy(datedEntries, e => e.date);

Object.fromEntries(
   Object.entries(entriesBydate).map(
     ([date, entries]) => ([
     date, 
     Object.fromEntries(entries.map(e => [e.asset, e.rolling_amount]))
   ]))
);
ready
procedural
const dateIndexedBalances = {};
  for (const entry of data) {
    if (!entry || !entry.date) {
      continue;
    }
    if (!dateIndexedBalances[entry.date]) {
      dateIndexedBalances[entry.date] = {};
    }
    if (entry.asset) {
      dateIndexedBalances[entry.date][entry.asset] = entry.rolling_amount;
    }
  }

ready
combo
const datedEntries = data.filter(e => !!e && !!e.date && !!e.asset)

const entriesBydate = Object.groupBy(datedEntries, e => e.date);

for(const date in entriesBydate) {
	entriesBydate[date] = Object.fromEntries(entriesBydate[date].map((e) => [e.asset, e.rolling_amount]));
}
ready
lodash
const entriesByDate = _.groupBy(data, e => e.date);

_.mapValues(entriesByDate, (entries) => _.reduce(entries, (acc, next) => {
	acc[next.asset] = next.rolling_amount
	return acc;
}, {}))
ready

Revisions

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