TrevirN
TrevirN

Reputation: 13

Sortable Bar Chart Not Working

Just starting learning D3 so this should probably be a basic fix. Anyway, I'm trying to make a simple bar chart sort with a click. Can't seem to figure it out after hours of searching.

My guess is its something to do with the transition or the scales, but I of course am a novice.

Here's the code.

<script src="https://d3js.org/d3.v4.min.js"></script>

<body>
  <script>
    var w = 600;
    var h = 250;

    var dataset = [{
        "namePlayer": "Player1",
        "mean": 37.4375
      },
      {
        "namePlayer": "Player2",
        "mean": 13.6937
      },
      {
        "namePlayer": "Player3",
        "mean": 26.7
      },
      {
        "namePlayer": "Player5",
        "mean": 20.0804
      },
      {
        "namePlayer": "Player6",
        "mean": 27.9235
      }
    ];

    var xScale = d3.scaleBand()
      .domain(dataset.map(function(d, i) {
        return d.namePlayer;
      }))
      .range([w, 0])
      .padding(0.05);
    var yScale = d3.scaleLinear()
      .domain([0, d3.max(dataset, function(d) {
        return d.mean;
      })])
      .range([h, 0]);
    var svg = d3.select("body")
      .append("svg")
      .attr("width", w)
      .attr("height", h);

    var xAxis = d3.axisBottom()
      .scale(xScale);



    svg.selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x", function(d) {
        return xScale(d.namePlayer);
      })
      .attr("width", xScale.bandwidth())
      .attr("y", function(d) {
        return yScale(d.mean);
      })
      .attr("height", function(d) {
        return h - yScale(d.mean);
      })
      .attr("fill", function(d) {
        return "rgb(0, 0, " + Math.round(d.mean * 10) + ")";
      })
      .on("click", function() {
        sortBars();
      })

    var sortBars = function() {
      svg.selectAll("rect")
        .sort(function(a, b) {
          return d3.ascending(a.mean, b.mean);
        })
        .transition()
        .delay(function(d) {
          return d.mean * 50;
        })
        .duration(1000)
        .attr("x", function(d) {
          return xScale(d.mean);
        });
    };
  </script>
</body>

Appreciate the help!

Upvotes: 1

Views: 30

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

Right now you're simply sorting a selection (and passing the wrong property to the xScale scale).

Instead of that, you should sort the data...

dataset.sort(function(a, b) {
    return d3.ascending(a.mean, b.mean);
});

... and pass it to the xScale:

xScale.domain(dataset.map(function(d, i) {
    return d.namePlayer;
}));

Then, you can use your transition.

Here is the updated code:

<script src="https://d3js.org/d3.v4.min.js"></script>

<body>
  <script>
    var w = 600;
    var h = 250;

    var dataset = [{
        "namePlayer": "Player1",
        "mean": 37.4375
      },
      {
        "namePlayer": "Player2",
        "mean": 13.6937
      },
      {
        "namePlayer": "Player3",
        "mean": 26.7
      },
      {
        "namePlayer": "Player5",
        "mean": 20.0804
      },
      {
        "namePlayer": "Player6",
        "mean": 27.9235
      }
    ];

    var xScale = d3.scaleBand()
      .domain(dataset.map(function(d, i) {
        return d.namePlayer;
      }))
      .range([w, 0])
      .padding(0.05);
    var yScale = d3.scaleLinear()
      .domain([0, d3.max(dataset, function(d) {
        return d.mean;
      })])
      .range([h, 0]);
    var svg = d3.select("body")
      .append("svg")
      .attr("width", w)
      .attr("height", h);

    var xAxis = d3.axisBottom()
      .scale(xScale);

    svg.selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x", function(d) {
        return xScale(d.namePlayer);
      })
      .attr("width", xScale.bandwidth())
      .attr("y", function(d) {
        return yScale(d.mean);
      })
      .attr("height", function(d) {
        return h - yScale(d.mean);
      })
      .attr("fill", function(d) {
        return "rgb(0, 0, " + Math.round(d.mean * 10) + ")";
      })
      .on("click", function() {
        sortBars();
      })

    var sortBars = function() {
      dataset.sort(function(a, b) {
        return d3.ascending(a.mean, b.mean);
      });
      xScale.domain(dataset.map(function(d, i) {
        return d.namePlayer;
      }));
      svg.selectAll("rect")
        .transition()
        .delay(function(d) {
          return d.mean * 50;
        })
        .duration(1000)
        .attr("x", function(d) {
          return xScale(d.namePlayer);
        });
    };

  </script>
</body>

Upvotes: 1

Related Questions