Marco
Marco

Reputation: 281

Optimizing javascript d3 statement

I am new to d3 and now using the log scale method to determine what color something must be. I think there must be a way to optimize the code code below with more features of d3. How can I do that?

var logScale = d3.scale.log().domain([1, 400]).range([0, 6]);
var colorIndex = Math.round(logScale(count));
var colors = ['#D6EAF8', '#A9DFBF', '#A2D9CE', '#F1948A', '#CD6155', '#A93226', '#7B241C'];
return colors[colorIndex];

Upvotes: 2

Views: 72

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

Technically speaking, what you're trying to create here is a threshold scale.

You could do it using d3.scale.threshold (D3 v3 in your case, or d3.scaleThreshold in v4). However, you will have to explicitly set the threshold, and the final code may be even uglier than what you have right now. Let's see it.

First, let's check the results of your code:

var logScale = d3.scale.log().domain([1, 400]).range([0, 6]);
var colors = ['#D6EAF8', '#A9DFBF', '#A2D9CE', '#F1948A', '#CD6155', '#A93226', '#7B241C'];

var result = d3.range(1, 401, 1).map(function(d) {
  var colorIndex = Math.round(logScale(d));
  return colors[colorIndex]
}).reduce(function(a, c) {
  if (c in a) {
    a[c]++
  } else {
    a[c] = 1
  }
  return a
}, {})

console.log(result)
<script src="https://d3js.org/d3.v3.min.js"></script>

That result is just an object that I'm using to store how many times each color appears if we call your code from 1 to 400, don't be terribly worried about it right now. Just have in mind that this is the result that we will try to reproduce using the threshold scale.

For using the threshold scale, the first step is setting the thresholds. For that, will have to use your log scale:

var logScale = d3.scale.log().domain([1, 400]).range([0, 6]);
var thresholds = [];

d3.range(0.5, 6.5, 1).forEach(function(d) {
  thresholds.push(logScale.invert(d))
});

console.log(thresholds)
<script src="https://d3js.org/d3.v3.min.js"></script>

Then, with those thresholds, we can use the threshold scale:

var thresholdScale = d3.scale.theshold()
    .range(['#D6EAF8', '#A9DFBF', '#A2D9CE', '#F1948A', '#CD6155', '#A93226', '#7B241C'])
    .domain(thresholds);

Finally, let's test our threshold scale, using the same result object:

var logScale = d3.scale.log().domain([1, 400]).range([0, 6]);

var thresholds = [];

d3.range(0.5, 6.5, 1).forEach(function(d) {
  thresholds.push(logScale.invert(d))
});

var thresholdScale = d3.scale.threshold()
  .range(['#D6EAF8', '#A9DFBF', '#A2D9CE', '#F1948A', '#CD6155', '#A93226', '#7B241C'])
  .domain(thresholds);

var result = d3.range(1, 401, 1).map(function(d) {
  return thresholdScale(d)
}).reduce(function(a, c) {
  if (c in a) {
    a[c]++
  } else {
    a[c] = 1
  }
  return a
}, {})

console.log(result)
<script src="https://d3js.org/d3.v3.min.js"></script>

As you can see, the same results... yey!

Was it worth it? Well, I'd say no: your code is smaller, cleaner, easier to understand and probably faster. Yet, I wrote this answer to show you that there is a threshold scale in D3, and to show you how to convert your logic into that scale.

Upvotes: 2

Related Questions