John Guinn
John Guinn

Reputation: 21

Horizontal bar chart index is off

I am creating a horizontal bar chart that shows data from a CSV file dynamically. I am getting an extra space where another value would be, but without any data. I only want the top 10 values(students) plus keys(counties) and it looks like it adds one.

Horizontal Bar chart image

As you can see it adds a space and a small rect at the top. I just want to remove both.

    // Parse Data
    d3.csv("FA18_geographic.csv", function(data) {  

        data.sort(function(a,b) {
          return d3.descending(+a.students, +b.students);
        });


        // find max county
        var max = d3.max(data, function(d) { return +d.students;} );

        var map = d3.map(data, function(d){return d.students; });
        pmax = map.get(max);



        // Add X axis
        var x = d3.scaleLinear()
        .domain([0, +pmax.students+500])
        .range([ 0, width]);
        svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x))
        .selectAll("text")
          .attr("transform", "translate(-10,0)rotate(-45)")
          .style("text-anchor", "end");


        // Y axis
        var count = 0
        var y = d3.scaleBand()
        .range([ 0, height ])
        .domain(
            data.map(function(d) { //only show top 10 counties
                count = count+1
                if (count<=10){
                return d.county;
             }})
            )
        .padding(.3);
        svg.append("g")
        .call(d3.axisLeft(y));



        //Bars                      //Something must be wrong in here???
        svg.selectAll("rect.bar")
        .data(data)
        .enter()
        .append("rect")
        .attr("x", x(0) )
        .attr("y", function(d) { return y(d.county); })
        .attr("width", function(d) { return x(d.students); })
        .attr("height", y.bandwidth() )
        .attr("fill", "#79200D");

    });

</script>

Upvotes: 1

Views: 87

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102198

Your problem lies here:

.domain(data.map(function(d) { //only show top 10 counties
    count = count + 1
    if (count <= 10) {
        return d.county;
    }
}))

The issue here has nothing to do with D3, that's a JavaScript issue: you cannot skip interactions using Array.prototype.map.

Let's show this with the basic demo below, in which we'll try to return the item only if count is less than 5:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let count = -1;

const domain = data.map(function(d) {
  count += 1;
  if (count < 5) {
    return d
  };
});

console.log(domain)

As you see, it will return several undefined. You only see one empty tick because D3 band scale treats all those values as a single undefined (domain values in band scales are unique).

There are several solutions here. You can, for instance, use Array.prototype.reduce:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let count = -1;

const domain = data.reduce(function(acc, curr) {
  count += 1;
  if (count < 5) {
    acc.push(curr)
  };
  return acc;
}, []);

console.log(domain)

Alternatively, you can use a filter after your map, or even a forEach to populate the array.

Finally, some tips:

  1. You don't need a count, since Array.prototype.map has an index (the second argument), just like Array.prototype.reduce (the third argument).
  2. You don't need to do count = count + 1, just do count += 1;
  3. You are skipping the first element, because a JavaScript array is zero-based. So, start count as -1 instead of 0.

Upvotes: 1

Related Questions