A multi-level groupBy for arrays inspired by D3’s nest operator.
Nesting allows elements in an array to be grouped into a hierarchical tree
structure; think of it like the GROUP BY
operator in SQL, except you can have
multiple levels of grouping, and the resulting output is a tree rather than a
flat table. The levels in the tree are specified by key functions.
If you’re the sort who’s offended by the mere sight of coffeescript, please see this gist instead.
Depends on lodash’s groupBy and mapValues:
_ = require 'lodash'
nest = (seq, iters...) ->
return seq unless iters.length
[first, rest...] = iters
_.mapValues _.groupBy(seq, first), (value) -> nest(value, rest...)
Input data to be nested:
data = [
type: "apple"
color: "green"
quantity: 1000
,
type: "apple"
color: "red"
quantity: 2000
,
type: "grape"
color: "green"
quantity: 1000
,
type: "grape"
color: "red"
quantity: 4000
]
Criteria for grouping:
byType = (d) -> d.type # group by type
byColor = (d) -> d.color # group by color
byQuantity = (d) -> d.quantity # group by quantity
Expected output when grouping by color
and quantity
:
expected =
green:
"1000": [
{ type: 'apple', color: 'green', quantity: 1000 },
{ type: 'grape', color: 'green', quantity: 1000 }
]
red:
"2000": [ {type: 'apple', color: 'red', quantity: 2000} ]
"4000": [ {type: 'grape', color: 'red', quantity: 4000} ]
deepEqual nest(data, byColor, byQuantity), expected
Expected output when grouping by type
and color
:
expected =
apple:
green: [ { "type": "apple", "color": "green", "quantity": 1000 } ]
red: [ { "type": "apple", "color": "red", "quantity": 2000 } ]
grape:
green: [ { "type": "grape", "color": "green", "quantity": 1000 } ]
red: [ { "type": "grape", "color": "red", "quantity": 4000 } ]
deepEqual nest(data, byType, byColor), expected
As mentioned above, this approach was inspired by D3’s nest operator.
D3’s nest
implementation is very powerful due to its method chaining approach,
enabling flexible combinations of nestings and rollups. See a quick demo
here.
Note that you don’t actually need to use/require all of D3 just to use the nest
operator. Use this extracted version of nest
if this is all you need for your project.