Question about d3 functions and variables

58 views
Skip to first unread message

Claudia Chen

unread,
Feb 1, 2018, 9:30:42 PM2/1/18
to d3-js
Hello!

So I'm programming a couple of physics simulations, but I feel like I end up repeating my code, since I'm trying to have it dynamically update the graph while the simulation is being played with. (I'm using vue for that)

My question is, is there an easier way to do this?

For example, for d3.line(), I can't seem to write one function that can take the data and use it for path data. Instead, I have to declare a line variable twice because the variable is scoped inside a function, but I can't make it a global variable because the line function I want has to access functions inside the vue app (which is a little bit self contained)

So instead of writing one function and being done with it, I have to write two variables... and then use the variables as functions.

Is there a way I can write these functions-esque variables as actual functions? In the past I've tried but it doesn't seem to work.

Same thing with d3.scale() and it's kinda hard to write dry code when I can't figure this out.

Thanks for reading.

Erik Stel

unread,
Feb 2, 2018, 2:17:20 AM2/2/18
to d3-js
Can you provide some sample code explaining your problem more specifically?

Claudia Chen

unread,
Feb 6, 2018, 5:23:50 PM2/6/18
to d3-js
Entire code file with redundancies: 


update: function(){
d3.select(".yaxis").call(d3.axisLeft(this.yScale()));
var yScale = this.yScale();
var line = d3.line()
.x(function(d){return xScale(d);})
.y(function(d){return yScale(app.varVelocity(d));});
svg.select(".velocity").attr("d", line(numberList));
d3.select(".marker").attr("cx", xScale(this.positionX)).attr("cy", yScale(this.varVelocity(app.positionX)));
var axisline = d3.line().x(function(d){return xScale(d.x);}).y(function(d){return yScale(d.y);});
svg.select(".velocitylines").attr("d", axisline([
{x: 0, y: this.varVelocity(this.positionX)},
{x: this.positionX, y: this.varVelocity(this.positionX)},
{x: this.positionX, y: 0}]));
},

Basically this simple question:

why can't you write a function like:

function xScale(number_to_be_scaled){
      return d3.scaleLinear().domain([0,100]).range([0,50]);
}

and call this function like a normal function?

or 

function line(data){
      return d3.line().x(function(d){return d.x;}).y(function(d){return d.y;});
}

and use the line as a function that can receive data?

It feels like I am forced to use variables, yet I'm using variables just like a function, so I'm just confused why I can't declare functions to use? 

david jankoski

unread,
Feb 7, 2018, 8:15:23 AM2/7/18
to d3-js
maybe i'm misinterpreting your question but the way you define it xscale _is_ a function and e.g. you can do the following:

var xscale = d3.scaleLinear().domain([0,100]).range([0, 50]);

then use it as

xscale(100);

or

d3.range(25).map(xscale);

Claudia Chen

unread,
Feb 7, 2018, 10:38:07 AM2/7/18
to d3-js
Yeah, I get that you can define the scale as a variable and use it as a function. That makes sense.

I'll try the second line you wrote the d3.range(25).map(xscale), I think that might work.

I want to know if it's possible to define the scale in a function, or do I always have to define it as a variable? I'm using vue, and I sometimes need dynamically updated axes (continuously updating). And when I update the axes, sometimes I have to update the lines in the graph, so then I have to define a temporary variable to get the scale, which is returned from a function. 

I just don't want to use a temporary variable because it feels like I'm writing repetitive code

Also, thanks for sticking with my question, I didn't explain it well the first time haha

Claudia Chen

unread,
Feb 7, 2018, 10:40:54 AM2/7/18
to d3-js
The d3.range(25).map(xscale) didn't work

b.j.k...@utwente.nl

unread,
Feb 7, 2018, 10:44:32 AM2/7/18
to d3...@googlegroups.com

You can change the scale’s properties in a function:

 

Function updateScale(newDomain, newRange) {

            myScale.domain (newDomain);

            myScale.range(newRange);

}

 

 

--

Barend Köbben

--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
d3-js+un...@googlegroups.com.
For more options, visit
https://groups.google.com/d/optout.

Claudia Chen

unread,
Feb 7, 2018, 11:17:12 AM2/7/18
to d3-js
The scale's properties are already being automatically updated, it works perfectly fine

To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+unsubscribe@googlegroups.com.
For more options, visit
https://groups.google.com/d/optout.

Erik Stel

unread,
Feb 8, 2018, 3:33:49 AM2/8/18
to d3-js
Claudia,

I don't see why using a temporary variable is resulting in "writing repetitive code". You seldom have to use temporary variables, you could just refer to the 'thing' you put into the temporary variable. At least I'm assuming you do not create a new range (or 'thing') everytime. (Or are you referring to "line"? Please read on ;-) The provided code does not show many.

You do create a temporary variable yScale ("var yScale = this.yScale()"). This might be required since "this" will be different inside most D3 functions being called. It will be assigned the current DOM-element when invoked. Is this the temporary variable you're talking about? You cannot really change this. If you have more than one occurrence of this situation you could create a temporary variable referring to 'this' and use that reference in the other function calls. Typically you would call this temporary variable "self" like so:
    var self = this;

    // And than use it like so:
    var line = d3.line()

.x(function(d){return xScale(d);})

.y(function(d){return self.yScale(app.varVelocity(d));});

If you are referring to the temporary variable 'line'. You can create a function for this. The function does have to have access to both scales. Since yScale seems to be connected to some other object, I'm not sure if this is possible. The xScale is not attached to this object. If yScale is not (globally) known, you could try and pass the scale as a parameter to the function. This could become something like (I kept it mostly looking like your code, you could however make it more compact. See comments.):
    function myLine(data, yScale) {
        var line = d3.line()
            .x(function(d) { return xScale(d); })                                // Actually the following would suffice: .x(xScale)
            .y(function(d) { return yScale(app.varVelocity(d)); })     // Assuming 'app' is globally known
        ;
        return line(data);                                                               // You could call this directly without the need for the temporary variable "line"
     }

And then use it like so:
    svg.select(".velocity").attr("d", myLine(numberList, this.yScale));    // I use "this" here since it is not used inside a function, but used directly as a 'constant'

If you'd need it inside a function use this:
    var self = this;
    svg.select(".velocity").attr("d", function() { return myLine(numberList, self.yScale); });

Does this help?

Cheers,
Erik

Claudia Chen

unread,
Feb 8, 2018, 10:46:02 AM2/8/18
to d3-js
that definitely helped! I totally forgot you could do that. 

one more question, for the comment " // Actually the following would suffice: .x(xScale)", I find it interesting that you don't need to write out the function(d) stuff, how does that work? Can I put any function into the parentheses? 

Erik Stel

unread,
Feb 8, 2018, 11:34:25 AM2/8/18
to d3-js
Claudia,

If the function has the same parameters (type and order of parameters are equal) you can use just the name.

The following function declarations are (almost ;-) similar. The second declaration might help in understanding that the name IS the function. So you do not need to write a new function (typically wrapping another function), just because a function is expected. Just name the (existing) function you need.

function myFunc1(x, y, z) {
    // Do something with x, y and z
}

var myFunc = function2(x, y, z) {
     // Do something with x, y and z
}

// Same way of calling them
myFunc1(1, 2, 3);
myFunc2(1, 2, 3);

Almost all D3-functions have the form: "function(d, i, nodes)" (but you do not need to use "i" and "nodes", so leaving these (or all) parameters out is fine too).
Creating a function with these parameters allows you to use these functions, just by name.

Remember that xScale (defined something like below) will actually create a function, since scales/ranges are generators (= functions) in D3.
    var xScale = d3.scaleLinear().domain([0,100]).range([0,50]);

Calling a function might result in a function being returned/answered. That's what the scales/ranges do. Like so:
    function myFunc() {
        return function(d) {
             // My special function doing something special
             return Math.abs(d);
        };
     }

     var x = myFunc();
     console.log(x(-3));    // 3 will be answered/shown

Good luck!

Cheers,
Erik
Reply all
Reply to author
Forward
0 new messages