0

Here I have a strange problem, I have a data table of the form:

[{grade:"a", count:1000},
{grade:"b", count:935},
.....]

but when I use the map function :

var v = data.map(function (d) { return d.count; });
console.log(v);

table v is found with the values ​​{0,0,0,0,0} If anyone has an idea where the problem may come from, I'm interested.

var svg = d3.select("svg"),
    margin = { top: 20, right: 20, bottom: 30, left: 40 },
    x = d3.scaleBand().padding(0.1),
    y = d3.scaleLinear();

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

g.append("g")
    .attr("class", "axis axis--x");

g.append("g")
    .attr("class", "axis axis--y");

g.append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", "0.71em")
    .attr("text-anchor", "end")
/*.text("Count of Product")*/;

var dm = data_manager();
var data = dm.load_histo_grade_data();

console.log(data);

x.domain(data.map(function (d) { return d.grade; }));
var v = data.map(function (d) { return d.count; });
console.log(v);
y.domain([0, d3.max(data.map(function (d) { return d.count; }))]);

draw(data);

function draw(theData) {

    var margin = {top: 10, right: 30, bottom: 30, left: 40},
 width = 300 - margin.left - margin.right,
 height = 200 - margin.top - margin.bottom;

    x.rangeRound([0, width]);
    y.rangeRound([height, 0]);

    g.select(".axis--x")
 .attr("transform", "translate(0," + height + ")")
 .call(d3.axisBottom(x));

    
    g.select(".axis--y")
 .call(d3.axisLeft(y));

    // ENTER
    g.selectAll(".bar")
 .data(theData)
 .enter().append("rect")
 .attr("class", "bar")
 .attr("x", function (d) { return x(d.grade); })
 .attr("y", function (d) {
     return y(d.count); })
 .attr("width", x.bandwidth())
 .attr("height", function (d) { return height - y(d.count); })
 .attr("fill", function(d) { return color_nutriscore_grade(d.grade); } );
}

Edit : The code for the function data_manager and load_histo_grade_data

function data_manager(){
    var dm = {};
    dm.filter = {
 WORLD:0,
 REGION:1
    };
    dm.choosen_filter = dm.filter.WORLD;
    dm.choosen_region = "Europe";
    dm.accept = function(row){
 return true;
    };
    dm.load_histo_grade_data = function(){
 data_to_update = [
     {grade:"a", count:0.0},
     {grade:"b", count:0.0},
     {grade:"c", count:0.0},
     {grade:"d", count:0.0},
     {grade:"e", count:0.0}
 ];
 d3.tsv("../tsv/hypotesis.tsv", function(error, data){
     if(error) throw error;
     var tmp = {
  "a":0,
  "b":1,
  "c":2,
  "d":3,
  "e":4,
     };
     for(var i = 1; i<data.length; i++){
  idx = tmp[data[i].grade];
  var v = data_to_update[idx].count;
  data_to_update[idx].count = (v + 1);
     }
 });
 return data_to_update;
    };
    dm.load = function(){
 dm.load_histo_grade_data();
    }
    return dm;
}

function color_nutriscore_grade(grade){
    switch(grade) {
    case '':  return d3.rgb(191, 191, 191);
    case 'a': return d3.rgb(  0, 191,   0);
    case 'b': return d3.rgb(115, 255,   0);
    case 'c': return d3.rgb(255, 204,   0);
    case 'd': return d3.rgb(255, 102,   0);
    case 'e': return d3.rgb(255,  25,   0);
    };
}
Cœur
  • 32,421
  • 21
  • 173
  • 232
litsubzu
  • 3
  • 2
  • 4
    The problem is likely caused by the array not looking like you think it looks. – Pointy Dec 26 '18 at 19:08
  • 3
    Is `load_histo_grade_data()` asynchronous? – Mark Dec 26 '18 at 19:10
  • The output for the consle log is : (5) […] ​0: Object { grade: "a", count: 1009 } ​1: Object { grade: "b", count: 935 } ​2: Object { grade: "c", count: 1514 } ​3: Object { grade: "d", count: 2198 } ​4: Object { grade: "e", count: 1690 } ​length: 5 : [] – litsubzu Dec 26 '18 at 19:15
  • 2
    @litsubzu [you can't necessarily trust that](https://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays). – Patrick Roberts Dec 26 '18 at 19:24
  • Your code looks like it should work (although you didn't post the complete code). It'd be helpful if you created a pen on codepen.io (etc) with the complete code. You can try stepping through with the debugger to get some clues about what is happening. – imjosh Dec 26 '18 at 19:28
  • @PatrickRoberts When I use the firefox debugger I have the same thing – litsubzu Dec 26 '18 at 19:51
  • Can you show us `data_manager()`? – robinsax Dec 26 '18 at 19:52
  • @robinsax Yep I edited my initial post – litsubzu Dec 26 '18 at 19:54
  • 3
    [`d3.tsv`](https://github.com/d3/d3-request#tsv) looks like it is asynchronous, that's where your problem is. – Herohtar Dec 26 '18 at 20:01
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Herohtar Dec 26 '18 at 20:10

1 Answers1

2

The problem here is that d3.tsv() is asynchronous, meaning it will run your callback "later". So you're returning the initial value of data_to_update (with all grades 0) from dm.load_histo_grade_data(), then later it is being populated with the correct values.

The reason your call to console.log(data) is outputting the populated array is because console.log has the fancy feature of displaying a live object, meaning it the object changes after it was logged, what you see in the console will change too.

The fix is to pass a callback into dm.load_histo_grade_data() instead of waiting for it to return, like so:

// In data_manager()
dm.load_histo_grade_data = function(callback){
    data_to_populate = [
        {grade:"a", count:0.0},
        {grade:"b", count:0.0},
        {grade:"c", count:0.0},
        {grade:"d", count:0.0},
        {grade:"e", count:0.0}
    ];
    d3.tsv("../tsv/hypotesis.tsv", function(error, data){
        if(error) throw error;
        var tmp = {
            "a":0,
            "b":1,
            "c":2,
            "d":3,
            "e":4,
        };
        for (var i = 1; i<data.length; i++){
            idx = tmp[data[i].grade];
            var v = data_to_populate[idx].count;
            data_to_populate[idx].count = (v + 1);
        }
        callback(data_to_populate)
    });
};

You can then use this as follows:

var dm = data_manager();
dm.load_histo_grade_data(function(data) {
    console.log(data);

    x.domain(data.map(function (d) { return d.grade; }));
    var v = data.map(function (d) { return d.count; });
    console.log(v);
    y.domain([0, d3.max(data.map(function (d) { return d.count; }))]);

    draw(data);
});

Side note: You should use camelCase instead of snake_case in JavaScript.

robinsax
  • 1,117
  • 3
  • 9