Reputation: 424
How I can access to data in multi-dimensional array in d3.js?
I have created this sparkline using one dimensional array. But what about when I want to create more sparklines using multi-dimensional array.
I have this effect
var canvasWidth = 200,
canvasHeight = 150;
var pathData = [
{ "x": 0, "y": 50},
{ "x": 20, "y": 20},
{ "x": 60, "y": 40},
{ "x": 80, "y": 30},
{ "x": 120, "y": 90},
{ "x": 150, "y": 70},
{ "x": 180, "y": 30},
{ "x": 190, "y": 90},
];
var widthScale = d3.scaleLinear()
.domain([0, 190])
.range([0, canvasWidth - 30]);
var heightScale = d3.scaleLinear()
.domain([0, 90])
.range([0, canvasHeight - 10]);
// Canvas
var canvas = d3.select("body")
.append("svg")
.attr("width", canvasWidth)
.attr("height", canvasHeight)
.append("g")
.attr("transform","translate(20)");
// _+_+_+_+_+_+_+_+ Basic of Data --> SVG Path _+_+_+_+_+_+_+_+ //
// Create accessor function
var pathFunction = d3.line()
.x(function(d) { return widthScale(d.x); })
.y(function(d) { return heightScale(d.y); })
.curve(d3.curveCatmullRom);
// Create <path> and append accessor function
var path = canvas.append("path")
.attr("d", pathFunction(pathData))
.attr("stroke", "red")
.attr("stroke-width", 2)
.attr("fill", "none");
// Add data points
var circles = canvas.selectAll("circle")
.data(pathData)
.enter()
.append("circle")
.attr("cx", function(d) { return widthScale(d.x) })
.attr("cy", function(d) { return heightScale(d.y) })
.attr("fill", "black")
.attr("r", 4);
This where I'm stuck because I cant add circles to each sparkline - `
var canvasWidth = 200,
canvasHeight = 150;
var pathData = [
[
{ "x": 0, "y": 50}, { "x": 20, "y": 20},
{ "x": 60, "y": 40}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
],
[
{ "x": 0, "y": 90}, { "x": 20, "y": 20},
{ "x": 30, "y": 80}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
],
[
{ "x": 0, "y": 40}, { "x": 20, "y": 20},
{ "x": 30, "y": 80}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
]
];
var widthScale = d3.scaleLinear()
.domain([0, 190])
.range([0, canvasWidth - 30]);
var heightScale = d3.scaleLinear()
.domain([0, 90])
.range([0, canvasHeight - 10]);
// Canvas
var container = d3.select(".main-container")
// Create accessor function
var pathFunction = d3.line()
.x(function(d) { return widthScale(d.x); })
.y(function(d) { return heightScale(d.y); })
.curve(d3.curveCatmullRom);
var canvas = container.selectAll("svg")
.data(pathData)
.enter()
.append("svg")
.attr("width", canvasWidth)
.attr("height", canvasHeight)
.append("g")
.attr("transform","translate(20)");
var sparklines = canvas.append("path")
.attr("d", pathFunction)
.attr("stroke", "#19ff9f")
.attr("stroke-width", 2)
.attr("fill","none");
var circles = canvas.selectAll("circle")
.data(pathData)
.enter()
.append("circle")
.attr("cx", function(d,i) { return widthScale(d.x); })
.attr("cy", function(d,i) { return widthScale(d.y); })
.attr("fill", "black")
.attr("r", 3);
`
CX and CY position are NaN. And also there are just 3 circles, not 8 as array length (http://prntscr.com/edp7hq)
Upvotes: 0
Views: 161
Reputation: 102219
You need just one modification:
Since you have 3 inner arrays, each SVG has one of such arrays bound as data. So, when it comes to append the circles, instead of...
var circles = canvas.selectAll("circle")
.data(pathData)
.enter()
.append("circle")
..., which points to the whole data array, it should be...
var circles = canvas.selectAll("circle")
.data(d => d)
.enter()
.append("circle")
... which makes the inner arrays (that is, the data bound to each SVG) as the data array for each group of circles.
Here is your working code with that modification only (also, you had a typo on heightScale
):
var canvasWidth = 200,
canvasHeight = 150;
var pathData = [
[
{ "x": 0, "y": 50}, { "x": 20, "y": 20},
{ "x": 60, "y": 40}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
],
[
{ "x": 0, "y": 90}, { "x": 20, "y": 20},
{ "x": 30, "y": 80}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
],
[
{ "x": 0, "y": 40}, { "x": 20, "y": 20},
{ "x": 30, "y": 80}, { "x": 80, "y": 30},
{ "x": 120, "y": 90}, { "x": 150, "y": 70},
{ "x": 180, "y": 30}, { "x": 190, "y": 90},
]
];
var widthScale = d3.scaleLinear()
.domain([0, 190])
.range([0, canvasWidth - 30]);
var heightScale = d3.scaleLinear()
.domain([0, 90])
.range([0, canvasHeight - 10]);
// Canvas
var container = d3.select("body")
// Create accessor function
var pathFunction = d3.line()
.x(function(d) { return widthScale(d.x); })
.y(function(d) { return heightScale(d.y); })
.curve(d3.curveCatmullRom);
var canvas = container.selectAll("svg")
.data(pathData)
.enter()
.append("svg")
.attr("width", canvasWidth)
.attr("height", canvasHeight)
.append("g")
.attr("transform","translate(20)");
var sparklines = canvas.append("path")
.attr("d", pathFunction)
.attr("stroke", "#19ff9f")
.attr("stroke-width", 2)
.attr("fill","none");
var circles = canvas.selectAll("circle")
.data(d=>d)
.enter()
.append("circle")
.attr("cx", function(d,i) { return widthScale(d.x); })
.attr("cy", function(d,i) { return heightScale(d.y); })
.attr("fill", "black")
.attr("r", 3);
<script src="https://d3js.org/d3.v4.min.js"></script>
Upvotes: 1
Reputation: 341
One useful way I've found to inspect a problem like this is to log your "d" value to the console:
.attr("cx", function(d,i) { console.log(d); return widthScale(d.x) })
That will probably highlight that, because you are now using a multidimensional array, the data bound to your circle elements doesn't provide you with immediate access to the data points anymore. Something like the following will probably work:
.attr("cx", function(d,i) { return widthScale(d[i].x) })
Upvotes: 0