4
votes

I am still struggling with this error as indicated in this earlier post on StackOverflow.com. I have isolated the cause of the problem, which is in my D3.js code it is unable to iterate through an 'object'. My raw data source is a RESTful web api. Using jQuery and a JavaScript function I am able to load the values into a variable called 'dataset'. When I output the contents of 'dataset' as an alert, or write it to an html element using jQuery all the data is there. Way too many D3.js examples use hard coded files of data. So it is important to note when I use a hard coded version of the same data everything works, but I am confined to using this dynamic virtual variable of data.

When I run the code it crashes at this function:

    dataset.forEach(function(d) {
        d.theTime = parseDate(d.theTime);
        d.theValue = +d.theValue;
    });

The error message is Uncaught TypeError: Cannot read property 'length' of undefined.

When I use console.log(typeof(dataset)) right before this function I get 'object'. If there are any D3.js mentors reading this what are my options for converting this data? I have explored several without success.

// ============= APPENDED 01 ========================

Because it has been requested here is the 'dataset', which can be viewed at the earlier post link (Parsing Time Series Data using D3.js) above:

    var dataset = [
    {'theTime': '2016/07/12 15:58:40', 'theValue': 1123.07275390625},
    {'theTime': '2016/07/12 16:21:10', 'theValue': 1055.6793212890625},
    {'theTime': '2016/07/12 16:45:40', 'theValue': 962.4850463867188},
    {'theTime': '2016/07/12 17:14:40', 'theValue': 831.2259521484375},
    {'theTime': '2016/07/12 17:55:10', 'theValue': 625.3046875}
    ];

// ============= APPENDED 02 ========================

In reference to Gerardo's question the 'dataset' variable is 'loaded' (I think) by virtue of the code is inside a '$.get' function that assembles the 'dataset' variable as shown here:

    //~ Populate the 'dataset':
    var dataset = [];
    $.get(url, function(data){
        var itemCount = data.Items.length;
        var commaCount = itemCount - 1;
        for(i=0; i<itemCount; i++){
            if(i == commaCount){
                dataset.push("{'theTime': '" + formattedDateTime(data.Items[i].Timestamp) + "', 'theValue': " + data.Items[i].Value + "}");
            }
            else {
                dataset.push("{'theTime': '" + formattedDateTime(data.Items[i].Timestamp) + "', 'theValue': " + data.Items[i].Value + "},");
            }
        }
    //~ ALL THE D3 CODE IS INSIDE THIS '$.get' FUNCTION
    });

// ============= APPENDED 03 ========================

I got it working!

I will post the revised working code below. But for anyone following this post I wanted to explain, what I found.

Mark suggested altering the dataset.push() to drop all the quotes. This gave me objects instead of strings. After some troubleshooting it finally displayed as expected (totally psyched!), and thanks Mark, your suggestions did the trick.

Here is the revised code:

    //~ Populate the 'dataset':
    var dataset = [];
    $.get(url, function(data){
        var itemCount = data.Items.length;
        var commaCount = itemCount - 1;
        for(i=0; i<itemCount; i++){
            dataset.push({theTime: formattedDateTime(data.Items[i].Timestamp), theValue: data.Items[i].Value});
        }

        var margin = {top: 20, right: 20, bottom: 30, left: 50};
        var width = 960 - margin.left - margin.right;
        var height = 500 - margin.top - margin.bottom;
        var parseDate = d3.time.format("%Y/%m/%d %H:%M:%S").parse;

        var x = d3.time.scale()
          .range([0, width]);

        var y = d3.scale.linear()
          .range([height, 0]);

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

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

        var line = d3.svg.line()
          .x(function(d) { return x(d.theTime); })
          .y(function(d) { return y(d.theValue); });


        var svg = d3.select("#myChart").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 + ")");

        dataset.forEach(function(d) {
            d.theTime = parseDate(d.theTime);
            d.theValue = parseFloat(d.theValue);
        });

         x.domain(d3.extent(dataset, function(d) { return d.theTime; }));
         y.domain(d3.extent(dataset, function(d) { return d.theValue;}));

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

         svg.append("g")
                 .attr("class", "y axis")
                 .call(yAxis)
                 .append("text")
                 .attr("transform", "rotate(-90)")
                 .attr("y", 6)
                 .attr("dy", ".71em")
                 .style("text-anchor", "end")
                 .text("M³/hr");

         svg.append("path")
                 .datum(dataset)
                 .attr("class", "line")
                 .attr("d", line);
    });

      //~~~ Format The Date:
      function formattedDateTime(dateAndTime) {
            var d = new Date(dateAndTime);
            var numDate = d.getDate();
            var numMonth = d.getMonth() + 1;
            var numYear = d.getFullYear();
            var numHours = d.getHours();
            var numMinutes = d.getMinutes();
            var numSeconds = d.getSeconds();
            numDate = (numDate < 10) ? "0" + numDate : numDate;
            numMonth = (numMonth < 10) ? "0" + numMonth : numMonth;
            numHours = (numHours < 10) ? "0" + numHours : numHours;
            numMinutes = (numMinutes < 10) ? "0" + numMinutes : numMinutes;
            numSeconds = (numSeconds < 10) ? "0" + numSeconds : numSeconds;

            return numYear + "/" + numMonth + "/" + numDate + " " + numHours + ":" + numMinutes + ":" + numSeconds;
      };
2
Do you have those keys in your object? Even better, can you paste or take a screenshot of that object?Gerardo Furtado
The problem is that dataset is an Object but you trying to use it like an Array. Like @gerardo-furtado mentioned, we need to see what that object looks like to determine the best way to iterate it. Since it is an Object, then using forEach will not work. Maybe Object.keys(dataset).forEach or dataset.items.forEach is correct...CaptEmulation
Yes, forEach will iterate an array of objects.Gerardo Furtado
It seems OK to me. I was about to tell you to try a bracket notation but it won't make any difference... Now I'm thinking about asynchrony: are you sure that you have the dataset loaded before the forEach?Gerardo Furtado
Do a console.log(dataset) after for(i=0; i<itemCount; i++){ and update your question with the results. It looks like you are building an array of strings in dataset and not an array of objects. Your push should look like this: dataset.push({theTime: formattedDateTime(data.Items[i].Timestamp), theValue: data.Items[i].Value});, not sure why are you building strings. I think your dataset really looks like var dataset = [ "{'theTime': '2016/07/12 15:58:40', 'theValue': 1123.07275390625}", ...]Mark

2 Answers

0
votes

If you're not sure whether you'll be getting an 'array of objects', or an 'objects with key/values', then I would use _.forEach in the library 'lodash' will iterate over an array of objects OR the key/values of an object.

0
votes

Based on the error message, Cannot read property 'length' of undefined the error is not coming from dataset.forEach, it is coming from somewhere where you are looking up the .length of something.

(Note: in Chrome dev tools you can inspect/expand the error in the console and see what file and line number it comes from).

If it's coming from any of the code you shared, then this is the offending line:

var itemCount = data.Items.length;

If data.Items happens to be undefined, you'd get this error. So insert a console.log(data) on the line right before the error'ing line, inspect the output, look for data.Items, which is likely not there. Your diagnosis should point you in the direction to fixing it.