csymill26
csymill26

Reputation: 23

dc.js - dataTable -- combining data rows

I am creating a dashboard, and the data format is as such:

var data = [
    {"id": 'CDE', "count": 1, "name": 'ajdkf', "type": 'E', "state": 'A'},
    {"id": 'CDE', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'A'},
    {"id": 'CDE', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'A'},
    {"id": 'CDE', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'A'},
    {"id": 'HFG', "count": 1, "name": 'ajdkf', "type": 'E', "state": 'B'},
    {"id": 'HFG', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'B'},
    {"id": 'HFG', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'B'},
    {"id": 'HFG', "count": 0, "name": 'ajdkf', "type": 'E', "state": 'B'},
    {"id": 'ABF', "count": 1, "name": 'ghedw', "type": 'G', "state": 'A'},
    {"id": 'ABF', "count": 0, "name": 'ghedw', "type": 'G', "state": 'A'},
    {"id": 'ABF', "count": 0, "name": 'ghedw', "type": 'G', "state": 'A'},
    {"id": 'ABF', "count": 0, "name": 'ghedw', "type": 'G', "state": 'A'},
    {"id": 'DEF', "count": 1, "name": 'huiqs', "type": 'E', "state": 'A'},
    {"id": 'DEF', "count": 0, "name": 'huiqs', "type": 'E', "state": 'A'},
    {"id": 'DEF', "count": 0, "name": 'huiqs', "type": 'E', "state": 'A'},
    {"id": 'DEF', "count": 0, "name": 'huiqs', "type": 'E', "state": 'A'},
    ...
    {"id": 'ABC', "count": 1, "name": 'asbsd', "type": 'D', "state": 'B'},
    {"id": 'ABC', "count": 0, "name": 'asbsd', "type": 'D', "state": 'B'},
    {"id": 'ABC', "count": 0, "name": 'asbsd', "type": 'D', "state": 'B'},
    {"id": 'ABC', "count": 0, "name": 'asbsd', "type": 'D', "state": 'B'}
];

I am trying to create a table that looks like:

Name   State A Count   State B Count
ajdkf  1               1 
ghedw  1               0
huiqs  1               0 
asbsd  0               1

I currently have:

<body>
<div style='clear:both;'>
  <table id="Stats">
    <thead>
        <tr class="header">
            <th> name </th>
            <th> State A Count </th>
            <th> State B Count </th>
        </tr>
    </thead>
  </table>
</div>
</body>

<script>
...
var dataTable = dc.dataTable("#Stats");
var dataTableDim = ndx.dimension(function(d) {return d.name;});
dataTable
    .dimension(dataTableDim)
    .group(function(d) {return d.name})
    .columns([
        function(d) { return d.name; },
        function(d) { if (d.state == 'A') {return +d.count;} else {return 0;} },
        function(d) { if (d.state == 'B') {return +d.count;} else {return 0;} },
    ]);
...
</script>

And it is giving me a table that looks like:

Name   State A Count   State B Count
asbsd
asbsd  0               1
asbsd  0               0
asbsd  0               0
asbsd  0               0
ajdkf
ajdkf  1               0
ajdkf  0               0 
ajdkf  0               0 
ajdkf  0               0
ajdkf  0               1 
ajdkf  0               0 
ajdkf  0               0 
ajdkf  0               0
huiqs 
huiqs  1               0 
huiqs  0               0 
huiqs  0               0 
huiqs  0               0 
ghedw
ghedw  0               0 
ghedw  1               0
ghedw  0               0
ghedw  0               0

What do I need to do to get the table that I desire? I would love to have under "State A" 2 'columns' (Count and Percent) and the same for State B. Something like:

Name        State A              State B
        Count     Percent    Count     Percent

jsfiddle: https://jsfiddle.net/56hgjgsz/14/

I couldn't get the fiddle to read external files, so I had to add them to the js.. sorry :( (https://jsfiddle.net/56hgjgsz/29/)

This "works" but doesn't do the row reduction in the table and the ordering is backwards -> same address but with /32/ instead of /14/ or /29/

Upvotes: 1

Views: 718

Answers (1)

Gordon
Gordon

Reputation: 20130

A couple of years after dc.js came out, people discovered that you can actually pass a group as the .dimension() for a data table. That's what you want here, since you are doing crossfilter reductions to get the state counts.

Here's an example. (source)

This is noted in the documentation here.

The method for reducing multiple values in a crossfilter group is standard:

var dataTableGroup = dataTableDim.group().reduce(
  function(p, v) {
    ++p.number;
    p.total  += +v.count;
    p.stateA += (v.state === 'A' ? v.count : 0);
    p.stateB += (v.state === 'B' ? v.count : 0);
    return p;
  },
  function(p, v) {
    --p.number;
    p.total  -= +v.count;
    p.stateA -= (v.state === 'A' ? v.count : 0);
    p.stateB -= (v.state === 'B' ? v.count : 0);
    return p;
  },
  function() {
    return {
      number: 0,
      total:  0,
      stateA: 0,
      stateB: 0
    }
  }
);

Then we can make a "fake dimension" for the data table to read the top or bottom N values, keeping only bins that have a value for either stateA or stateB. Note we have to pass N through, unlike for fake groups:

function remove_empty_bins_dt(source_group) {
  return {
    top: function(N) {
      return source_group.top(N).filter(function(d) {
        return d.value.stateA !== 0 || d.value.stateB !== 0;
      });
    },
    bottom: function(N) { // hacky but should work 
      return source_group.top(Infinity).filter(function(d) {
        return d.value.stateA !== 0 || d.value.stateB !== 0;
      }).slice(-N).reverse();
    }

  };
}

Again, the data table is unique in dc.js in that it is designed to read data from a crossfilter dimension not a group. So we need to pass this fake dimension to .dimension() instead of .group() (which means something completely different sigh)

Working fork of your fiddle: https://jsfiddle.net/gordonwoodhull/d86foxyq/5/

(if I understood it correctly)

One could generalize this to look at an arbitrary number of fields, but I'm out of time for today. 😉

Upvotes: 2

Related Questions