简体   繁体   English

如何使用D3导航嵌套的JSON对象?

[英]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?? 有人可以指导我做错什么吗?


My incoming JSON object: 我的传入JSON对象:

{
    "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
        }]
    }]
}

My main 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>
        );
    }
}

My react-faux-dom D3 component 我的react-faux-dom D3组件

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是无法将minmax应用于的对象。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM