Alternatives for using forEeach() loop while conve

2019-04-11 16:17发布

问题:

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;
      };

回答1:

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.



回答2:

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.