Detect which type of d3 scale I am working with

826 Views Asked by At

I'm writing some charting components using D3. Some of these components are deliberately generic, so they can be re-used in various scenarios.

In one function, I receive a scale object as an argument. I want to take slightly different actions depending on whether this is a linear scale, a time scale, or an ordinal scale.

However I cannot easily see how to detect which type of scale I've got.

  • instanceof does not work - these objects aren't create with new and don't have a prototype set. They are Function objects with additional attributes added to them.

  • typeof returns 'object' in all cases (naturally)

  • at least in the case of ordinal scales, there are some additional attributes that I can use for detection, so I can use:

    if (scale.rangeBound) {
        // do something specific to ordinal scales
    }
    

    but that does not seem to be the case for different types of quantative scales, which seem to have identical sets of attributes.

  • I can add some attribute to each scale I create indicating it's type, but I would prefer not to as it reduces the generality of the functions I am creating (they would require scales passed to them which had these attributes added). Also I would risk clashing with some attribute added by a future version of D3.

In fact this question could be extended to many of the objects within D3. Is there any way to tell them apart?

2

There are 2 best solutions below

2
On

You could insulate yourself against code changes by coding your check against the behaviour of the scales, than it's internals

function checkType(scale) {
    var s = scale.copy();
    if (s.domain([1, 2]).range([1, 2])(1.5) === 1)
        return "ordinal";
    else if (s.domain([1, 2]).range([1, 2]).invert(1.5) === 1.5)
        return "linear";
    else if (s.domain([1, 2]).range([1, 2]).invert(1.5) instanceof Date)
        return "time";
    else
        return "not supported";
}

Note, that this would still trip up for other / custom scales that have the same behaviour.

Fiddle - http://jsfiddle.net/enon2rjs/

2
On

I played around a little, and found something that might be useful. I'm not sure how reliable this is, but comparing the string version of a method that all three scales share seems to give the desired result.

The statement

console.log(""+ordinal.domain);

Prints some representation of the function ordinal.domain (which, at least for me, is the source code). This should be the same for any two instances of ordinal.domain. It seems to work, but it does feel like there should be a better way.

var ordinal = d3.scale.ordinal();
var ordinal2 = d3.scale.ordinal();
var time = d3.time.scale();
var range = d3.scale.linear();


ordinal.domain([1,2]);
ordinal.range([2,3])

console.log(""+ordinal.domain == ""+ordinal2.domain); //true
console.log(""+time.domain == ""+ordinal.domain); //false
console.log(""+range.domain == ""+time.domain); //false