Stefanie Gauss
Stefanie Gauss

Reputation: 737

Why d3.domain() has only one element when I use the whole data array as domain?

I have done something more complicated before using d3.histogram and bins, but in the following situation, I am not sure why the domain() would not give back the array but only the first element? Can't we use the data array and each element in it as the "key" for the domain?

const data = [
  { name: "A", value: 1 },
  { name: "B", value: 5 },
  { name: "C", value: 7 },
  { name: "D", value: 3 },
  { name: "E", value: 9 },
];

const scaleX = d3
  .scaleBand()
  .domain(data)
  .range([0, 1000]);

console.log("scaleX.domain()", scaleX.domain());
console.log("scaleX.range()", scaleX.range());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.2/d3.js" integrity="sha512-6G9G63qZLxEcLqVyklArI/2ZgWK1HdKMU9yWbQUJC/eSsLsaY0NZKv4JDW4ltw1Z9sEvFB3DP54GzOIkIJjyLg==" crossorigin="anonymous"></script>

Upvotes: 1

Views: 756

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102198

You did it right when you passed an array to the domain() method...

.domain(data)

...however, that's not enough: arrays can contain numbers, strings, functions, other arrays etc. The problem with a band (or ordinal) scale is that you cannot have objects as the individual elements of the array. We can see why by having a look at the source code:

scale.domain = function(_) {
    if (!arguments.length) return domain.slice();
    domain = [], index = new Map();
    for (const value of _) {
      const key = value + "";
      if (index.has(key)) continue;
      index.set(key, domain.push(value));
    }
    return scale;
};

If you have a look at for (const value of _) { const key = value + "";, which internally generate the unique keys for a JavaScript Map, you'll see that this simply won't work for objects:

const obj1 = {foo:42};
const obj2 = {bar:17};
console.log(obj1 + "");
console.log(obj2 + "");
console.log(obj1 + "" === obj2 + "");

The most important information in the snippet above is the true in the last console.log: all objects, even if different, become the same thing for the scale when it generates the keys for the Map (remember, band scales cannot have duplicated values).

Because of that, the continue here...

if (index.has(key)) continue;

...will always happen, so your domain effectively has just one element.

Upvotes: 3

Related Questions