As byNation is passed as a variable into outerRollup it’s also available within the innerRollup, correct?
Exactly, yes.
How is the value of byNation specified as the variable for the outerRollup?
That’s just how d3.rollup
is defined. To get a bit more insight into that, lets consider how d3.rollup
is implemented. The actual source code of it is a bit intense, but here is a version that for the purposes of this discussion is the close enough:
function toyRollup(array, rollupFunction, keyFunction) {
// Separate all the values of arrays into groups, based on the keyFunction
let groups = new Map();
for (let element of array) {
let key = keyFunction(element);
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(element);
}
// For each group, use the rollupFunction to combine it into a single value
let rolledUpData = new Map();
for (let [key, group] of groups.entries()) {
let combinedValue = rollupFunction(group);
rolledUpData.set(key, combinedValue);
}
return rolledUpData;
}
If we then consider just a one layered version of the problem where we just want to calculate the number of athletes in each nation:
function rollupFunction(athletesOfOneNation) {
return athletesOfOneNation.length;
}
function keyFunction(athlete) {
return athelete.nation;
}
let numberOfAthletesInEachNation = toyRollup(athletes, rollupFunction, keyFunction);
then we can substitute the value into the definition of toyRollup
. This is what the solution to the question about the number of athletes in each nation would look like without d3.rollup:
function calculateNumberOfAthletesInEachNation(athletes) {
let athletesGroupedByNation = new Map();
for (let athlete of athletes) {
let key = athlete.nation;
if (!athletesGroupedByNation.has(key)) {
athletesGroupedByNation.set(key, []);
}
athletesGroupedByNation.get(key).push(athlete);
}
let numberOfAthletesByNation = new Map();
for (let [nation, group] of athletesGroupedByNation.entries()) {
numberOfAthletesByNation.set(nation, group.length);
}
return numberOfAthletes;
}
Hopefully that helps make sense of how the data moves around during the rollup process! I also hope that now you can see the appeal of having an API that can turn that complicated function into something as compact as this:
const numAthletesByNation = d3.rollup(athletes, ds => ds.length, d => d.nation);