calling a function as data in D3.js - javascript

Im just getting started on d3.js and was going through Nick's source code on github here and got stuck at the part where he is passing a function as data into d3.js.
The var x in the function assigned to next var gets incremented from 0 to the loop counter as i show in the jsbin link below. I cant quite wrap my head around how x gets incremented automatically and how does it know the loop counter that it needs to get incremented upto everytime.
the next variable is called from >> newdata from the >>render function ?
I just setup a jsbin here

This part:
.data(newData);
is simply going to call the newData function and bind the return to the selection.
So each call to render in the setInterval simply pushes the next function into his data array.
This part then:
selection.attr("class", "v-bar")
.style("height", function (d, i) {
return d(i) + "px"; // <- E
})
.select("span")
.text(function(d, i){
return d(i); // <- F
});
Calls d which is the next function for each element in the data array. It's passing the index position in the data array.
So the first render call is:
15 + 0 * 0;
Second is:
15 + 0 * 0;
15 + 1 * 1;
Third is:
15 + 0 * 0;
15 + 1 * 1;
15 + 2 * 2;

First, for simplification, this
var selection = d3.select("#container").selectAll("div")
.data(newData); // <- D
is just like writing
var arrayOfFunctions = newData();
var selection = d3.select("#container").selectAll("div")
.data(arrayOfFunctions); // <- D
So, for example, calling this code 3 times (via setInterval) builds up arrayOfFunctions like this:
arrayOfFunctions = [
function (x) { return 15 + x * x; },
function (x) { return 15 + x * x; },
function (x) { return 15 + x * x; }
]
(Note: it's not literally like that, because in actuality they're just pointers to the same function next)
So nothing about that increments x. But once it binds those functions to DOM elements (via data(arrayOfFunctions) and runs through this bit:
selection.attr("class", "v-bar")
.style("height", function (d, i) {
return d(i) + "px"; // <- E
})
d is function (x) { return 15 + x * x; } and i (which is 0, 1, or 2) is passed in as x to that function when it calls d(i).
And that's what essentially increments x.

Related

D3: Passing extra arguments to attr functions inside a selection.join()

I've some code inside a selection.join() pattern:
const nodeWidth = (node) => node.getBBox().width;
const toolTip = selection
.selectAll('g')
.data(data)
.join(
(enter) => {
const g = enter
.append('g')
g.append('text')
.attr('x', 17.5)
.attr('y', 10)
.text((d) => d.text);
let offset = 0;
g.attr('transform', function (d) {
let x = offset;
offset += nodeWidth(this) + 10;
return `translate(${x}, 0)`;
});
selection.attr('transform', function (d) {
return `translate(${
(0 - nodeWidth(this)) / 2
},${129.6484} )`;
});
},
(update) => {
update
.select('text')
.text((d) => d.text);
let offset = 0;
update.attr('transform', function (d) {
let x = offset;
offset += nodeWidth(this) + 10;
return `translate(${x}, 0)`;
});
selection.attr('transform', function (d) {
return `translate(${
(0 - nodeWidth(this)) / 2
},${129.6484} )`;
});
}
);
as you can see, in the enter and update section I need to call a couple of functions to calculate several nodes transformations. In particular, the code stores in the accumulation var offset the length of the previous text element. This properly spaces text elements (ie, text0 <- 10 px -> text1 <- 10 px -> ...).
As you can see, the "transform functions" in the enter and update section are identical. I'm trying to define them just in one place and call them where I need. E.g.,
(update) => {
update.attr('transform', foo);
selection.attr('transform', bar);
}
However, I cannot refactor the code this way because it looks like I cannot pass in neither the offset value nor this to the function passed to attr().
Is there a way to do it?
EDIT:
As per Gerardo Furtado's hint (if I got it right), you can define foo as follows:
const foo = function(d, i, n, offset) {
let x = offset;
offset += nodeWidth(n[i]) + 10;
return `translate(${x}, 0)`;
}
then in the selection.join¡ you have to call foo this way:
(update) => {
let offset = 0;
update.attr('transform', (d, i, n) => foo(d, i, n, offset));
}
However, refactoring this way, offset is ever equal to 0. A possibile solution here: https://stackoverflow.com/a/21978425/4820341
Have a look at Function.prototype.bind().
const doSomething = (d) => {
return `translate(${
(0 - nodeWidth(this)) / 2
},${129.6484} )`;
}
Calling the function inside (enter) and (update)
selection.attr('transform', doSomething.bind(d));
This way the function gets executed in the current scope.
I guess this is what you are looking for. Please be aware that I could not test my code!

How to visually conceptualize this Javascript recursive function?

I want to visually understand what happens when this recursive code is run. I don't understand how the end result is 9.
In my mind, the f(x - 1) will iterate until it returns 5, then you add 1 which equals 6.
let f = x => {
if (x === 0) {
return 5
}
return 1 + f(x - 1)
}
let y = f(4)
console.log(y)
Thank you for your time
You have the reasoning backwards. It isn't that one gets added once at the very end, one gets added after each resursive call returns. Think about this line:
return 1 + f(x - 1)
Once the recursive f call returns, one is added to that result. Then this recursive call returns, and one is added again. That keeps happening until the initial call returns.
Since one is added once per recursive call, and it will recurse four times, and the base case returns 5, this function ends up basically just calculating
1 + 1 + 1 + 1 + 5 == 9
You could take a level for an indention of a log and look which level has an input and return value.
function f(x, level = 0) {
console.log(level, '>>'.repeat(level + 1), x);
var v = x === 0
? 5
: 1 + f(x - 1, level + 1);
console.log(level, '<<'.repeat(level + 1), v);
return v;
}
console.log('result', f(4));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript Functions within Array

Is there a way to print out the [ith] element of the array with the [ith] function's argument? for example, print out the returned value of 2nd element of the array with input (3) as function's argument
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
It's not exactly clear what you are looking for. I assume that you want to call the ith function passing j as an argument.
To access the ith element of an Array, you use the square brackets post-around-fix indexing operator [] and pass the index inside the square brackets, which looks like this: ary[idx]. To call a function, you use the round parentheses post-around-fix function call operator () and pass the argument inside the round parentheses, which looks like this: func(arg).
Like this:
puzzlers[2](3);
There's really nothing puzzling about this, if you understand what an array is and what a function is.
It sounds like you're looking for this, but the question is a bit cryptic.
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var secondFunction = puzzlers[1];
var result = secondFunction(3);
alert(result);
//or short
alert(puzzlers[1](3));

Why is this iterator returning the following values? [duplicate]

This question already has answers here:
Where argument of first next() call goes? [duplicate]
(2 answers)
Closed 6 years ago.
I was reading this article about javascript generators, and I reached the following snippet:
function *foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var it = foo( 5 );
// note: not sending anything into `next()` here
console.log( it.next() ); // { value:6, done:false }
console.log( it.next( 12 ) ); // { value:8, done:false }
console.log( it.next( 13 ) ); // { value:42, done:true }
I don't understand the purpose of the first it.next(). After executing it, this line, shouldn't the iterator be paused at var z = yield (y / 3), with y having the value of 6? Shouldn't it.next(12) supply the param for yield (y / 3), and z be 4 after this? I don't understand why the result of the function is not 5 + 12 + 4. It's somehow as if the first it.next() is ignored. Is this the case? Can someone please shed some light?
This may help
function *foo(x) {
var y = 2 * (yield (x + 1));
// x = 5 (from foo(5) ) so it yields 6
// next(12) => var y = 2 * (12) == 24
var z = yield (y / 3);
// from above y = 24 so yields 8
// next(13) => var z = 13
return (x + y + z);
// 5 + 24 + 13 == 42 (and done: true)
}
You might want to add some logging statements to see what is going on:
function *foo(x) {
console.log("starting");
var y = 2 * (yield (x + 1));
console.log("y", y);
var z = yield (y / 3);
console.log("z", z);
return (x + y + z);
}
var it = foo( 5 );
console.log("it", it);
console.log(it.next());
console.log(it.next(12));
console.log(it.next(13));
logs
it {next: …}
starting
{ value:6, done:false }
y 24
{ value:8, done:false }
z 13
{ value:42, done:true }
As you can see, calling foo(5) only creates the generator object, but does not yet start it. Only the first call to it.next() will do that, returning the result from the first yield. This first call does not take any argument, because it's not accessible inside the generator function anyway.
Only the second call to .next(12), here passing in a value, will then resume the generator code, with that passed-in value being the result of the yield expression (which is then multiplied by 2).

Code execution not linear (?) in D3 for loop

I'm doing a pretty trivial thing in D3, but I'm running into weird issues with the order of code execution. I'm taking an array of image names, looping through them, and adding each svg to a parent svg as a g element. All that works great. Here's the code that's doing so:
var names = ['a', 'b', 'c']
var over = d3.select('#overlay');
var w = 20;
for (var i = names.length - 1; i >= 0; i--) {
var x = w * i + w
console.log('1. '+ x)
d3.xml(names[i]+'.svg', function(error, xml) {
console.log('2. '+ x)
over.append('g')
.attr('transform','translate('+ x +','+ 20 +')')
.html(xml.getElementsByTagName('svg')[0].innerHTML)
})
}
However, I'm running into issues with the transforms all being the same, and decided to log x to check it out. The log should be this:
1. 60
2. 60
1. 40
2. 40
1. 20
2. 20
Instead, it's logging this:
1. 60
1. 40
1. 20
2. 20
2. 20
2. 20
What could possibly be making this run out of order like that?? Is the d3.xml call asynchronous? I can't figure it out.
d3.xml() is asynchronous function and is not started in sequence as expected but little later which is enough that loop is over and x has value 20.
You have to make closure for value x using for example:
(function (x) {
d3.xml(names[i]+'.svg', function(error, xml) {
console.log('2. '+ x)
over.append('g')
.attr('transform','translate('+ x +','+ 20 +')')
.html(xml.getElementsByTagName('svg')[0].innerHTML)
});
})(x)
That way you will get in console log:
1. 60
1. 40
1. 20
2. 60
2. 40
2. 20

Categories