Skip to content

Instantly share code, notes, and snippets.

@draptik
Last active June 25, 2016 23:00
Show Gist options
  • Save draptik/3562d891a03da60a65e0b731937b1840 to your computer and use it in GitHub Desktop.
Save draptik/3562d891a03da60a65e0b731937b1840 to your computer and use it in GitHub Desktop.
multi-level nesting (aka groupby) in javascript using d3.js (adapted from http://learnjsdata.com/group_data.html)
var expect = require('chai').expect;
var d3 = require('d3');
describe('app', function () {
var myData = [
{ countryCode: 'DE', areaName: 'DE1', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'DE', areaName: 'DE1', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'DE', areaName: 'DE1', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'DE', areaName: 'DE2', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'DE', areaName: 'DE2', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'DE', areaName: 'DE2', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'DE', areaName: 'DE3', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'DE', areaName: 'DE3', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'DE', areaName: 'DE3', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'ES', areaName: 'ES1', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'ES', areaName: 'ES1', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'ES', areaName: 'ES1', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'ES', areaName: 'ES2', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'ES', areaName: 'ES2', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'ES', areaName: 'ES2', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'ES', areaName: 'ES3', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'ES', areaName: 'ES3', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'ES', areaName: 'ES3', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'JP', areaName: 'JP1', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'JP', areaName: 'JP1', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'JP', areaName: 'JP1', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'JP', areaName: 'JP2', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'JP', areaName: 'JP2', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'JP', areaName: 'JP2', speciesName: 's3', totalScore: 3000 },
{ countryCode: 'JP', areaName: 'JP3', speciesName: 's1', totalScore: 1000 },
{ countryCode: 'JP', areaName: 'JP3', speciesName: 's2', totalScore: 2000 },
{ countryCode: 'JP', areaName: 'JP3', speciesName: 's3', totalScore: 3000 }
];
it('should do what I want (using d3)', function () {
var scoreBySpeciesAndCountry = d3.nest()
.key(function (d) { return d.speciesName; })
.key(function (d) { return d.countryCode; })
.rollup(function (v) { return d3.sum(v, function (d) { return d.totalScore; }) })
.map(myData);
expect(scoreBySpeciesAndCountry).to.eql(
{
s1: { DE: 3000, ES: 3000, JP: 3000 },
s2: { DE: 6000, ES: 6000, JP: 6000 },
s3: { DE: 9000, ES: 9000, JP: 9000 }
}
)
});
it('should do what I want (vanilla js solution....)', function () {
var myMap = function (d) {
function uniq(value, index, self) {
return self.indexOf(value) === index;
};
var output = d.map(obj => obj.speciesName).filter(uniq).map(speciesName => {
return [speciesName, d.filter(obj => obj.speciesName == speciesName).map(obj => obj.countryCode).filter(uniq).map(countryCode => {
return [countryCode, d.filter(obj => obj.speciesName == speciesName && obj.countryCode == countryCode).reduce((sum, obj) => sum + obj.totalScore, 0)];
}).reduce((hash, result) => (hash[result[0]] = result[1]) && hash, {})];
}).reduce((hash, result) => (hash[result[0]] = result[1]) && hash, {});
return output;
};
expect(myMap(sampleData)).to.eql(
{
s1: { DE: 3000, ES: 3000, JP: 3000 },
s2: { DE: 6000, ES: 6000, JP: 6000 },
s3: { DE: 9000, ES: 9000, JP: 9000 }
}
);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment