0

I'm trying to sort a horizontal stacked bar chart in d3, but keep coming up with undefined values which mess up my sorting. I was referencing Cyril's code but I keep getting "a.total = undefined". (It comes in the later half of the code, I've commented it out for you guys.)

Other than making sure this code sorts properly, my question is:

  1. Why is it undefined? Am I referencing attributes of different datasets wrong?
  2. If so, how does one access attributes of their respective datasets without being confined to being inside a function?

I am entirely new to d3 and javascript so any comments would be of great help and I appreciate any efforts to help! :)

//the stacks
var headers = ["AR_Score_2015", "ER_Score_2015", "FS_Score_2015", "CF_Score_2015", "IF_Score_2015", "IS_Score_2015"];

var margin = {
    top: 20,
    right: 50,
    bottom: 30,
    left: 20
  },
  width = 800 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

var x = d3.scale.linear()
  .rangeRound([0, width]);

var y = d3.scale.ordinal()
  .rangeRoundBands([0, height], .2);

var z = d3.scale.category10();

var color = d3.scale.ordinal().range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")

var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left");

//***Canvas setup***/
var svg = d3.select("body").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var csv = "Uni,AR_Score_2015,ER_Score_2015,FS_Score_2015,CF_Score_2015,IF_Score_2015,IS_Score_2015\n\
A,400,400,400,400,400,382\n\
B,400,400,394.4,400,399.6,304\n\
C,400,400,400,374.8,384.8,386.4\n\
D,400,400,398,399.6,390.4,291.2\n\
E,399.2,358.4,400,400,360.8,340.8\n\
F,400,400,400,355.6,391.2,386.4\n\
G,399.6,399.2,394.4,352,382,399.6\n\
H,399.6,400,399.6,318.4,400,400\n\
I,399.6,396,314.4,395.2,400,392\n\
J,399.6,385.2,375.2,366,293.6,326.4\n\
K,400,394,371.6,400,188.8,272.8\n\
L,400,400,371.6,315.6,400,370\n\
M,381.2,390,376.8,346,400,376\n\
N,400,400,400,285.2,359.6,262\n\
O,388.8,296,400,339.6,386.4,284.8\n\
P,399.6,386.8,295.6,388.4,360.8,264\n\
Q,395.2,389.6,400,322,211.6,266.4\n\
R,380.8,383.6,359.6,310,381.6,392.8\n\
S,398.8,392,341.2,286,372.4,391.6\n\
T,400,397.6,400,268,132,359.2\n\
U,396.4,378.8,323.2,281.6,353.2,369.2\n\
V,398.4,398,350,336.8,191.2,144.4\n\
W,400,399.6,187.6,399.6,387.2,317.2\n\
X,400,398.8,296.8,358.4,229.6,196\n\
Y,377.2,367.2,263.2,357.6,400,388\n\
Z,385.6,338.4,399.2,341.6,60.8,229.6";
var csvBase64 = btoa(csv);
//***CSV START***/
d3.csv("data:text/plain;base64," + csvBase64, type, function(error, scores) {
  if (error) throw error;

  scores.forEach(function(d) {
    d.Overall_Score_2015 = +d.Overall_Score_2015;
    d.AR_Score_2015 = +d.AR_Score_2015;
    d.ER_Score_2015 = +d.ER_Score_2015;
    d.FS_Score_2015 = +d.FS_Score_2015;
    d.CF_Score_2015 = +d.CF_Score_2015;
    d.IF_Score_2015 = +d.IF_Score_2015;
    d.IS_Score_2015 = +d.IS_Score_2015;
  });


  //***Stacking***/

  var layers = d3.layout.stack()(headers.map(function(c) {
    return scores.map(function(d) {
      return {
        x: d.Uni,
        y: d[c]
      };
    });
  }));
  var count = 0;
  var i = 0;

  var layers2 = layers.map(function(scores) {

    return scores.map(function(d) {
      d.width = 0;
      var l = scores.length;
      if (count == l * 5 + i) {
        d.total = d.y0;
        i++;
      }
      count++;
      return {
        x: d.y,
        y: d.x,
        x0: d.y0,
        parent: d
      };
    });
  });


  //***Drawing***/

  y.domain(layers2[0].map(function(d) {
    return d.y;
  }));
  x.domain([0, d3.max(layers2[layers2.length - 1], function(d) {
      return d.x / 4 + d.x0 / 4;
    })])
    .nice();

  color.domain(d3.keys(scores[0]).filter(function(key) {
    return key !== "Uni";
  }));

  var layer = svg.selectAll(".layer")
    .data(layers2)
    .enter().append("g")
    .attr("class", "layer")
    .attr("transform", function(d) {
      return "translate(0," + 0 + ")";
    })
    .style("fill", function(d, i) {
      return z(i);
    })
    .attr("class", "stack");

  layer.selectAll("rect")
    .data(function(d) {
      return d;
    })
    .enter().append("rect")
    .attr("y", function(d) {
      return y(d.y);
    })
    .attr("x", function(d) {
      return x(d.x0 / 4);
    })
    .attr("height", y.rangeBand())
    .attr("width", function(d) {
      d.parent.width += x(d.x) - x(d.x0);
      return -(x(d.x0 / 4) - x(d.x / 4 + d.x0 / 4));
    })
    .on("click", function() {
      sortBars();
    });

  svg.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  svg.append("g")
    .attr("class", "axis axis--y")
    .attr("transform", "translate(0,0)")
    .call(yAxis);


  //***Sorting***/

  function sortBars() {

    //***Problem here***//
    var z = y.domain(layers2.sort(function(a, b) {
          console.log("a.total = " + a.total, a, b);
          return b.total - a.total;
        })
        .map(function(d) {
          return d.layers2;
        }))
      .copy();

    //*** This is the code I was referencing from ***/
    var y0 = y.domain(layers2.sort(this.id == "test" ?
          function(a, b) {
            return b.total - a.total;
          } :
          function(a, b) {
            return d3.ascending(a.total, b.total);
          })
        .map(function(d) {
          return d.layers2;
        }))
      .copy();
    console.log(y0);

    svg.selectAll(".stack")
      .sort(function(a, b) {
        console.log("y0(a.total) = " + z(a.total));
        console.log("y0(b.total) = " + z(b.total));
        return z(a.total) - z(b.total);
      });

    var transition = svg.transition().duration(750),
      delay = function(d, i) {
        return i * 50;
      };

    transition.selectAll(".stack")
      .delay(delay)
      .attr("transform", function(d) {
        return "translate(0," + z(d.total) + ")";
      });

    transition.select(".y.axis")
      .call(yAxis)
      .selectAll("g")
      .delay(delay);
  }
});

function type(d) {
  headers.forEach(function(c) {
    d[c] = +d[c];
  });
  return d;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
miensol
  • 39,733
  • 7
  • 116
  • 112
Amanda.T
  • 1
  • 1

1 Answers1

0

The mdn documentation about undefined says:

A primitive value automatically assigned to variables that have just been declared or to formal arguments for which there are no actual arguments.

However if it doesn't help you much check this answer out.

You're getting a.total = undefined becuase of this expression:

if (count == l * 5 + i) {
  d.total = d.y0;
  i++;
}

Which only sets total property if the condition is true.

Community
  • 1
  • 1
miensol
  • 39,733
  • 7
  • 116
  • 112
  • Thank you for pointing out the tip about undefined! I didn't know about that! However, I still face problems. My `z` continues to generate a static value of 75 which resulted in my bars transforming a uniform distance in the same direction, namely `z(a.total) = 75` and `z(b.total) = 75`. How does the declaration of `z` lead to this number? – Amanda.T Jan 15 '16 at 20:06