[英]How to navigate nested JSON object with D3?
In my REACT application, I am reading in a JSON response (using axis ) which I am passing to a component using react-faux-dom to try and recreate Mike Bostock's Multi-Series Line Chart (using D3 v4). 在我的REACT应用程序中,我正在读一个JSON响应(使用axis ),我正在使用react-faux-dom将其传递给组件以尝试重新创建Mike Bostock的多系列折线图 (使用D3 v4)。
Running console.log(apiData)
in my component shows the object as I would expect - however, I've tried everything I can think of and the best I can get is empty X and Y axes (and my "scratchpad" <g>
element that I am trying to use to get some insight of what I am working with). 在我的组件中运行
console.log(apiData)
显示我所期望的对象-但是,我已经尝试了所有我能想到的东西,并且我能得到的最好的结果是空的X和Y轴(以及我的“便签本” <g>
我试图用来获取我正在使用的内容的一些见解的元素)。
Can someone guide me to what I am doing wrong please?? 有人可以指导我做错什么吗?
{
"roles": [
{
"AA": [
{
"date": "20150101",
"total": 6.0
},
{
"date": "20150201",
"total": 14.5
},
{
"date": "20150301",
"total": 14.5
}],
"AB": [
{
"date": "20150301",
"total": 1.6
},
{
"date": "20150401",
"total": 1.6
},
{
"date": "20150501",
"total": 7.24
}]
}]
}
App.js
file calling the component: App.js
文件调用了该组件: import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
import { MyD3ReactComponentExport } from './D3Timeseries'
class App extends Component {
/* ... assign API response to "apiResponse" */
render() {
const {
apiResponse
} = this.state
return (
<div className="App">
<button onClick={this.requestData}>Refresh Data</button>
<MyD3ReactComponentExport apiData={apiResponse} />
</div>
);
}
}
import React from 'react'
import * as d3 from 'd3'
import PropTypes from 'prop-types'
import { withFauxDOM } from 'react-faux-dom'
class MyD3ReactComponent extends React.Component {
constructor (props) {
super(props)
this.renderD3 = this.renderD3.bind(this)
this.updateD3 = this.updateD3.bind(this)
}
componentDidMount () {
this.renderD3()
}
componentDidUpdate (prevProps, prevState) {
if (this.props.data !== prevProps.data) {
this.updateD3()
}
}
render () {
return (
<div>
<h2>Amazing timeseries:</h2>
{this.props.myChart}
</div>
)
}
renderD3() {
const {
apiData
} = this.props
let faux = this.props.connectFauxDOM('div', 'myChart');
let svg = d3.select(faux).append('svg')
.attr("width",960)
.attr("height",500);
let margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let parseTime = d3.timeParse("%Y%m%d");
let x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
let line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.total); });
// ----- PASS apiData RESPONSE OBJECT INTO HERE ------
if (apiData) {
console.log( apiData )
x.domain(d3.extent( d3.map(apiData.roles, function(d) { return parseTime(d.date) }) ));
y.domain([
d3.min( apiData.roles, function(c) { return d3.min(c, function(d) { return d.total; }); }),
d3.max( apiData.roles, function(c) { return d3.max(c, function(d) { return d.total; }); }),
]);
z.domain( Object.keys(apiData) );
g.append("text").attr("y",height/3).attr("x", width/3).text( ">>>" + apiData.roles.map( function(d) { d }).map(function (e) { return e } ).map(function (f) { return f } ) + "<<<" ) ;
} else {
g.append("text").attr("y",height/2).attr("x", width/2).text( "No API DATA!!!" );
}
// ----Everything below here is 'stock' Bostock D3 code
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0,"+ height +")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Total resource");
let city = g.selectAll(".city")
.data(apiData)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
city.append("text")
.datum(function(d) { return {id: d.key, value: d.values[d.values.length - 1]}; })
.attr("transform", function(r) { return function(d) { return "translate(" + x(d.date) + "," + y(d.total) + ")"; } })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
// function type(d, _, columns) {
// d.date = parseTime(d.date);
// for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
// return d;
// }
}
updateD3() {
this.props.animateFauxDOM(800)
d3.select('text').text(this.props.title)
}
}
MyD3ReactComponent.defaultProps = {
myChart: 'loading'
};
MyD3ReactComponent.propTypes = {
// title: PropTypes.string.isRequired,
apiData: PropTypes.object.isRequired
}
const MyD3ReactComponentExport = withFauxDOM(MyD3ReactComponent);
export { MyD3ReactComponentExport }
The issue with setting the domains was that the roles
array consists of an object for which you were trying to map
, find d3.min
and find d3.max
which resulted in invalid values as those functions work only on ARRAYS. 设置域的问题在于,
roles
数组由您要为其map
的对象,找到d3.min
和找到d3.max
,这会导致无效值,因为这些函数仅适用于ARRAYS。
For example, 例如,
y.domain([
d3.min( apiData.roles, function(c) { return d3.min(c, function(d) { return d.total; }); }),
d3.max( apiData.roles, function(c) { return d3.max(c, function(d) { return d.total; }); }),
]);
In the above scenario, with your apiData
the variable c
is an object to which min
, max
cannot be applied. 在上述情况下,对于您的
apiData
,变量c
是无法将min
, max
应用于的对象。
Suggestion : Use as many console.logs as possible and add breakpoints using Chrome console or any debugger. 建议 :使用尽可能多的console.logs并使用Chrome控制台或任何调试器添加断点。
In order to do that, I'm first formatting the data in the following way: 为了做到这一点,我首先以以下方式格式化数据:
// format data
var data = [];
apiData.roles.forEach(function(d) {
var keys = Object.keys(d);
keys.forEach(function(k) {
data.push({key: k, values: d[k]});
})
});
This is how the new formatted data looks: 这是新格式化的数据的外观:
[
{
"key": "AA",
"values": [
{
"date": "20150101",
"total": 6
},
{
"date": "20150201",
"total": 14.5
},
{
"date": "20150301",
"total": 14.5
}
]
},
{
"key": "AB",
"values": [
{
"date": "20150301",
"total": 1.6
},
{
"date": "20150401",
"total": 1.6
},
{
"date": "20150501",
"total": 7.24
}
]
}
]
Using this data, the chart can be created as follows: (I've commented out the texts part: please use the formatted data to fix that) 使用此数据,可以按以下方式创建图表:(我已经注释掉了文本部分:请使用格式化的数据来修复该问题)
var apiData = { "roles": [ { "AA": [ { "date": "20150101", "total": 6.0 }, { "date": "20150201", "total": 14.5 }, { "date": "20150301", "total": 14.5 } ], "AB": [ { "date": "20150301", "total": 1.6 }, { "date": "20150401", "total": 1.6 }, { "date": "20150501", "total": 7.24 }] }] }; // format data var data = []; apiData.roles.forEach(function(d) { var keys = Object.keys(d); keys.forEach(function(k) { data.push({key: k, values: d[k]}); }) }); //console.log(data); let svg = d3.select('div#chart').append('svg') .attr("width",960) .attr("height",500); let margin = {top: 20, right: 80, bottom: 30, left: 50}, width = svg.attr("width") - margin.left - margin.right, height = svg.attr("height") - margin.top - margin.bottom, g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); let parseTime = d3.timeParse("%Y%m%d"); let x = d3.scaleTime().range([0, width]), y = d3.scaleLinear().range([height, 0]), z = d3.scaleOrdinal(d3.schemeCategory10); let line = d3.line() .curve(d3.curveBasis) .x(function(d) { return x(parseTime(d.date)); }) .y(function(d) { return y(d.total); }); // ----- PASS apiData RESPONSE OBJECT INTO HERE ------ if (apiData) { // console.log( apiData ) x.domain([ d3.min( data, function(c) { return d3.min(c.values, function(d) { return parseTime(d.date); }); }), d3.max( data, function(c) { return d3.max(c.values, function(d) { return parseTime(d.date); }); }) ]); // console.log(x.domain()); y.domain([ d3.min( data, function(c) { return d3.min(c.values, function(d) { return +d.total; }); }), d3.max( data, function(c) { return d3.max(c.values, function(d) { return +d.total; }); }), ]); //console.log(y.domain()); z.domain( data.map(function(d) { return d.key; }) ); /* g.append("text").attr("y",height/3).attr("x", width/3).text( ">>>" + apiData.roles.map( function(d) { d }).map(function (e) { return e } ).map(function (f) { return f } ) + "<<<" ) ; */ // ----Everything below here is 'stock' Bostock D3 code g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0,"+ height +")") .call(d3.axisBottom(x)); g.append("g") .attr("class", "axis axis--y") .call(d3.axisLeft(y)) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("fill", "#000") .text("Total resource"); let city = g.selectAll(".city") .data(data) .enter().append("g") .attr("class", "city"); city.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return z(d.key); }); /* city.append("text") .datum(function(d) { return {id: d.key, value: d.values[d.values.length - 1]}; }) .attr("transform", function(r) { return function(d) { return "translate(" + x(d.date) + "," + y(d.total) + ")"; } }) .attr("x", 3) .attr("dy", "0.35em") .style("font", "10px sans-serif") .text(function(d) { return d.key; }); */ } else { g.append("text").attr("y",height/2).attr("x", width/2).text( "No API DATA!!!" ); }
.city path { fill: none; }
<script src="https://d3js.org/d3.v4.min.js"></script> <div id="chart"> </div>
Hope this helps. 希望这可以帮助。 :)
:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.