Reputation: 2074
I have an array of arrays like so.
data = [
[
{x: 1, y: 40},
{x: 2, y: 43},
{x: 3, y: 12},
{x: 4, y: 60},
{x: 5, y: 63},
{x: 6, y: 23}
], [
{x: 1, y: 12},
{x: 2, y: 5},
{x: 3, y: 23},
{x: 4, y: 18},
{x: 5, y: 73},
{x: 6, y: 27}
], [
{x: 1, y: 60},
{x: 2, y: 49},
{x: 3, y: 16},
{x: 4, y: 20},
{x: 5, y: 92},
{x: 6, y: 20}
]
];
I can find the maximum y value of data with a nested d3.max() call:
d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.y;
});
});
I'm struggling to understand how this code actually works. I know the second argument of the d3.max() function specifies an accessor function - but I'm confused into how exactly calling d3.max() twice relates with the accessor function.
I guess what I'm asking for is a walkthrough of how javascript interprets this code. I've walked through it on the console but it didn't help unfortunately.
Upvotes: 1
Views: 1316
Reputation: 26181
Wow..! intriguing question really. Just for some sporting purposes here is an ES6 resolution of this problem by invention of an array method called Array.prototype.maxByKey()
So here you can see how in fact it's implemented by pure JS.
Array.prototype.maxByKey = function(k) {
var m = this.reduce((m,o,i) => o[k] > m[1] ? [i,o[k]] : m ,[0,Number.MIN_VALUE]);
return this[m[0]];
};
var data = [
[{x: 1, y: 40},{x: 2, y: 43},{x: 3, y: 12},{x: 4, y: 60},{x: 5, y: 63},{x: 6, y: 23}],
[{x: 1, y: 12},{x: 2, y: 5},{x: 3, y: 23},{x: 4, y: 18},{x: 5, y: 73},{x: 6, y: 27}],
[{x: 1, y: 60},{x: 2, y: 49},{x: 3, y: 16},{x: 4, y: 20},{x: 5, y: 92},{x: 6, y: 20}]
],
maxObj = data.map(a => a.maxByKey("y")).maxByKey("y");
console.log(maxObj);
Here is the story of what's going on in this piece of code. We will find the index of the object by reducing. Our reduce method uses an initial value, which is array [0,Number.MIN_VALUE]
, which at index 0 has 0 and at index 1 position has the smallest possible number in JS. Initial values are set to the first argument. So here m
starts with the initial value. Reduce will walk over the array items (objects in our case) one by one and each time o
will be assigned to the current object and the last argument i
is of course the index of the position we are currently working on. k is provided to our function as the key that we will be using to test the max value upon.
So there is this simple ternary comparison o[k] > m[1] ? [i,o[k]] : m
which means check current object property given by k
(o[k]
) if it is less than m[1]
(where m
is [0,Number.MIN_VALUE]
in the first turn) return m
as [i,o[k]]
(check how ternaries return result) if it is not less than m[1]
then return m
as it is. And at the end of the walk we will be reduced down to [index of the element with max k property value, the value of that k property]
in that array.
So as you see it is very simple.
Upvotes: 0
Reputation: 2155
Sometimes it's all about the naming of the variables:
// the outer function iterates over the outer array
// which we can think of as an array of rows
d3.max(data, function(row) {
// while the inner function iterates over the inner
// array, which we can think of as an array containing
// the columns of a single row. Sometimes also called
// a (table) cell.
return d3.max(row, function(column) {
return column.y;
});
});
You can find the source code for the d3.max function here: https://github.com/d3/d3.github.com/blob/8f6ca19c42251ec27031376ba9168f23b9546de4/d3.v3.js#L69
Upvotes: 3