Home > Net >  d3.groups by multiple keys given array of of keys
d3.groups by multiple keys given array of of keys

Time:05-09

See working fiddle here: https://jsfiddle.net/0qgtbk9r/9/

Say I have an array of objects for data, and the user has supplied multiple keys to group by. I want to utilize d3.groups to group by those keys.

For my fiddle example, I know I can chain the two keys "manually" to get what I want:

var gps = d3.groups(objs, d => d.Eye, d => d.Sex)

But I'm hard coding d.Eye / d.Sex...

So my question is how to get to same result if I supply an array of the keys to do the grouping by... something like this:

var gb_keys = ['Eye','Sex']
var gps = d3.groups(obj,d=>gb_key.map(x=>d.x)//Doesn't work but just a random idea/thought

This is similar/almost duplicate to Group by with multiple fields using d3.js but the questions are pretty old and I'm wondering if a d3-based solution exists out there now. ?

CodePudding user response:

d3.flatGroup and d3.flatRollup were introduced in v7 which address the issue of the nested structure tackled in the other question you referenced.

For a user-defined array of keys, you can use this approach in d3.flatGroup (and d3.groups etc):

var fields = ['Sex','Eye']
var gps = d3.flatGroup(
  objs,
  ...fields.map(k => d => d[k]) // instead of d => d.Sex and d => d.Eye etc
)

Working example:

var fields = ['Sex','Eye']
var gps = d3.flatGroup(
  objs,
  ...fields.map(k => d => d[k])
)
console.log(gps)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<script>
const ds = `Sex,Age,Eye,Height
M,43,blue,190
M,31,brown,200
M,50,blue,175
M,58,blue,210
M,26,blue,181
M,22,brown,202
M,59,green,189
M,52,brown,198
M,26,green,203
M,54,brown,201
M,29,brown,202
M,44,brown,178
F,58,brown,135
F,56,green,169
F,41,brown,155
F,23,brown,131
F,34,green,151
F,43,green,155
F,39,brown,176
F,42,blue,174
F,38,green,176
F,31,green,149
F,41,green,161
F,37,blue,178
F,35,brown,157
F,33,brown,150
F,41,blue,138
F,33,blue,147
F,21,green,144
F,47,brown,176
F,22,brown,169
F,22,brown,164
F,53,blue,144`
var objs = d3.csvParse(ds)
</script>

To address a requirement of an array of objects rather than an array of arrays you can do this:

var fields = ['Sex','Eye']
var gps = d3.flatGroup(
  objs,
  ...fields.map(k => d => d[k])
);

var fields2 = fields.concat('values');
var gps2 = gps.map(
  gp => Object.fromEntries(
    fields2.map((_, i) => [fields2[i], gp[i]])
  )
);

Working example:

var fields = ['Sex','Eye']
var gps = d3.flatGroup(
  objs,
  ...fields.map(k => d => d[k])
);

var fields2 = fields.concat('values');
var gps2 = gps.map(
  gp => Object.fromEntries(
    fields2.map((_, i) => [fields2[i], gp[i]])
  )
);
console.log(gps2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<script>
const ds = `Sex,Age,Eye,Height
M,43,blue,190
M,31,brown,200
M,50,blue,175
M,58,blue,210
M,26,blue,181
M,22,brown,202
M,59,green,189
M,52,brown,198
M,26,green,203
M,54,brown,201
M,29,brown,202
M,44,brown,178
F,58,brown,135
F,56,green,169
F,41,brown,155
F,23,brown,131
F,34,green,151
F,43,green,155
F,39,brown,176
F,42,blue,174
F,38,green,176
F,31,green,149
F,41,green,161
F,37,blue,178
F,35,brown,157
F,33,brown,150
F,41,blue,138
F,33,blue,147
F,21,green,144
F,47,brown,176
F,22,brown,169
F,22,brown,164
F,53,blue,144`
var objs = d3.csvParse(ds)
</script>

  • Related