Skip to content

Instantly share code, notes, and snippets.

@thorizer
Forked from joyrexus/README.md
Created April 27, 2020 21:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thorizer/5008aa92d88008cbd7ac45a0505eaf71 to your computer and use it in GitHub Desktop.
Save thorizer/5008aa92d88008cbd7ac45a0505eaf71 to your computer and use it in GitHub Desktop.
Nested grouping of arrays

nest.js

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.

See this fiddle for live demo.

Implementation

Depends on lodash's groupBy and mapValues:

_ = require('lodash');

var nest = function (seq, keys) {
    if (!keys.length)
        return seq;
    var first = keys[0];
    var rest = keys.slice(1);
    return _.mapValues(_.groupBy(seq, first), function (value) { 
        return nest(value, rest)
    });
};

module.exports = nest;

Usage

Input data to be nested:

var 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 }
];

Key functions used for grouping criteria:

var byType = function(d) {
  return d.type;
};

var byColor = function(d) {
  return d.color;
};

var byQuantity = function(d) {
  return d.quantity;
};

First Example

Expected output when grouping by color and quantity:

var 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 }
    ]
  }
};

Nest by key name:

deepEqual(nest(data, ['color', 'quantity']), expected);

Nest by key functions:

deepEqual(nest(data, [byColor, byQuantity]), expected);

Second Example

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 } ]
  }
};

Nest by key names:

deepEqual(nest(data, ['type', 'color']), expected);

Nest by key functions:

deepEqual(nest(data, [byType, byColor]), expected);
var nest = require('nest');
var assert = require('assert');
var eq = assert.deepEqual;
// input data to be nested
var 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 }
];
/* FIRST EXAMPLE */
// expected output, grouping by `color` and `quantity`
var 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 }
]
}
};
// key functions used for grouping criteria
var byType = function(d) {
return d.type;
};
var byColor = function(d) {
return d.color;
};
var byQuantity = function(d) {
return d.quantity;
};
// nest by key name
eq(nest(data, ['color', 'quantity']), expected);
// nest by key function
eq(nest(data, [byColor, byQuantity]), expected);
/* SECOND EXAMPLE */
// expected output, 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 } ]
}
};
// nest by key names
eq(nest(data, ['type', 'color']), expected);
// nest by key function
eq(nest(data, [byType, byColor]), expected);
_ = require('lodash');
var nest = function (seq, keys) {
if (!keys.length)
return seq;
var first = keys[0];
var rest = keys.slice(1);
return _.mapValues(_.groupBy(seq, first), function (value) {
return nest(value, rest)
});
};
module.exports = nest;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment