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; };
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 usingforEach
will not work. MaybeObject.keys(dataset).forEach
ordataset.items.forEach
is correct... – CaptEmulationforEach
will iterate an array of objects. – Gerardo Furtadoconsole.log(dataset)
afterfor(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 likevar dataset = [ "{'theTime': '2016/07/12 15:58:40', 'theValue': 1123.07275390625}", ...]
– Mark