1

I have a nested json like this:

(47) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0:
day: "2020-03-14"
total:
confirmed: 81
recovered: 9
deaths: 2
active: 70
__proto__: Object
statewise: Array(37)
0:
state: "Andaman and Nicobar Islands"
confirmed: 0
recovered: 0
deaths: 0
active: 0
__proto__: Object
1: {state: "Andhra Pradesh", confirmed: 1, recovered: 0, deaths: 0, active: 1}
.......

enter image description here

Structure is like this:

<array consiting daywise data>
    day
    total
     -confirmed
     -recovered
     -deaths
     -active
    statewise
       <array defining states having below items>
           - state
           - confirmed
           - recovered
           - deaths

Thing is, I want to make a line graph showing total cases(confirmed) trend with dates for states like this:

enter image description here

code:

  <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.7/crossfilter.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.2.1/dc.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/reductio/1.0.0/reductio.js"></script>

 <script>

  const cases_chart = dc.lineChart("#line-chart");
  var log = console.log; 
  d3.json('https://api.rootnet.in/covid19-in/unofficial/covid19india.org/statewise/history').then(function(json_data) {
  var data = json_data.data.history;
  log("=====>", data);

    cf = crossfilter(data); // Main crossfilter objects
    var cases_bar_d = cf.dimension(function(d) { 
      console.log('---------------------->',new Date(d.day));
      return d.day});

    var cases_bar_g = cases_bar_d.group().reduceSum(function(d){ 
      log("statewise:", d.statewise);

      return d.statewise});
   });
</script>

Actually there are 37 items in state and I know how to solve this solution. I have to create a composite chart. But creating 37 groups and then feed them to composite chart will be a lengthy process. Is there any other way of solving this? I can create groups individually by returning d.statewise[0].confirmed,.....d.statewise[36].confirmed

I want to make this cases_bar_g to be like this:

{key: <date>, <state_name1>: <confirmed1>, .....<state_name36>: <confirmed36> }

Then It will be easier to create a chart.

Edit1:

Seems like I found a similar question with the same nested pattern. But having hard time to implement it as a line chart.

Edit2:

I managed to flatten the array using for state confirmed cases.

    data.forEach(function(d,i){

      log("!!!!", i);

      for (var j=0; j<d.statewise.length;j++)
      {
          log("@@@:",map_state[d.statewise[j].state],d.statewise[j].state , d.statewise[j].confirmed);

         d[map_state[d.statewise[j].state]] = d.statewise[j].confirmed;
     }
   });

It look like this now: enter image description here

Although now I can access the element directly to plot it. But I think this is not a good approach. I should have done it via dimension and group concept.

Pygirl
  • 12,969
  • 5
  • 30
  • 43

1 Answers1

1

Flattening is often a good idea when dealing with crossfilter. It should work fine, and probably won't affect performance.

But I agree it's probably not necessary here.

You should be able to use a custom reduction to read the data from the statewise array and update multiple fields of a value object in each bin.

Off the top of my head, without being able to test, maybe something like

var cases_bar_g = cases_bar_d.group().reduce(
  // add
  (p, v) =>
    (v.statewise.forEach(({state, confirmed}) => p[state] = (p[state] || 0) + confirmed), p),
  // remove
  (p,v) =>
    (v.statewise.forEach(({state, confirmed}) => p[state] -= confirmed), p),
  // init
  () => ({})
);

Now that I look at it, it's not really any simpler than flattening, but I agree its "nicer" somehow.

We tell crossfilter:

  • each bin should be initialized with an empty object
  • each time you add a row to a bin, go through statewise and create or add an entry for that state in the bin's value object, starting with 0 and incrementing by confirmed
  • each time you remove a row, decrement the state by confirmed

Building a composite using a loop

If you continue down this path instead of using the series chart, it's still pretty easy to create the child charts for the composite.

You just need an array of state names, which you can pull from the first row:

const states = data[0].statewise.map(d => d.state);

All the child charts will use the same dimension and group; we'll just use a different value accessor to pull the data.

We'll map the state names to an array of child charts and compose them:

composite.compose(states.map(
  state => dc.lineChart(composite)
    .dimension(cases_bar_d)
    .group(cases_bar_g)
    .valueAccessor(kv => kv.value[state])
    // need .x() and .xUnits and such here
));
Gordon
  • 19,811
  • 4
  • 36
  • 74
  • this gives undefined`v.statewise.forEach(({state, confirmed}) => p[state] = (p[state] || 0) + confirmed)` The inner statement does give the value `p[state] = (p[state] || 0) + confirmed`. – Pygirl Apr 30 '20 at 07:16
  • On google searching by `.forEach reduce dc js` stumbled upon your old [answer](https://stackoverflow.com/a/27570219/6660373) It should be `return p;` `v.statewise.forEach(({state, confirmed}) => p[state] = (p[state] || 0) + confirmed); return p;` . Will try to implement using the solution you have provided me. Thanks for the help :) – Pygirl Apr 30 '20 at 07:22
  • I am having now multiple value. So if I want to create multiple lines do I have to create mutliple line graphs say 37 line graphs and then compose it? or is there any other way of dointg it also? – Pygirl Apr 30 '20 at 08:17
  • Thanks for the correction, will edit my answer. Yes you would have to create 37 line charts but you can use loops or `.map()` instead of messy code. Alternately with the flattened data you could use the series chart. – Gordon Apr 30 '20 at 09:30
  • Yeah I was looking into series chart but if I have flattened data how to pass it? I looked into some of the question [1](https://stackoverflow.com/questions/51095906/how-can-i-fill-gaps-in-a-dc-js-series-chart?rq=1), [2](https://github.com/dc-js/dc.js/blob/develop/web-src/examples/series.html). Still need to figure out how to pass. – Pygirl Apr 30 '20 at 09:33
  • Using this solution managed to create Series chart but it look like this: https://snipboard.io/qRfoaY.jpg it got plotted for one state(assam). Will give a try first. because there are so many elements in the value so I am unable to map it to the series chart. – Pygirl Apr 30 '20 at 09:36
  • Not sure what you mean. Normally with the series chart you would create a dimension with a “multi key” - an array with, say, state and date. Then tell the series chart how to split (nest) the data. – Gordon Apr 30 '20 at 09:38
  • You would not use the code in this answer for the series chart. You would go back to the flattened data. – Gordon Apr 30 '20 at 09:40
  • So that means If I use via this group method then I have to use a composite chart?. So by flattening the array and using the series chart will be easier and clean approach that means? I thought value element too has a key value pair. So by any way if I could apply something and then I will be able to use serieschart for this one. Thanks for clearing this out. Will go for the flattening the data approach. – Pygirl Apr 30 '20 at 09:41
  • 1
    The series chart is a composite chart underneath, it’s just a different way of creating the same thing. But yes the input is a different format. With the code in this answer you would have to loop over the data and create the charts to compose. I guess I should add that to my answer. – Gordon Apr 30 '20 at 09:46
  • 1
    It's open to debate which approach is simpler. FWIW I added a section showing how you would create the child charts using an array of state names. – Gordon Apr 30 '20 at 10:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212849/discussion-between-pygirl-and-gordon). – Pygirl Apr 30 '20 at 11:10