Skip to content

Instantly share code, notes, and snippets.

@joyrexus
Created March 28, 2014 16:59

Revisions

  1. J. Voigt revised this gist Mar 28, 2014. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -57,6 +57,7 @@ var byColor = function(d) {

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


    @@ -116,12 +117,12 @@ expected = {
    Nest by key names:

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

    Nest by key functions:

    ```js
    eq(nest(data, [byType, byColor]), expected);
    deepEqual(nest(data, [byType, byColor]), expected);
    ```

  2. J. Voigt created this gist Mar 28, 2014.
    127 changes: 127 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,127 @@
    # nest.js

    A multi-level [groupBy](http://underscorejs.org/#groupBy) for arrays inspired by D3's [nest](https://github.com/mbostock/d3/wiki/Arrays#-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](http://jsfiddle.net/V7an5/3/) for live demo.


    ## Implementation

    Depends on lodash's [groupBy](http://lodash.com/docs#groupBy) and [mapValues](http://lodash.com/docs#mapValues):

    ```js
    _ = 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:

    ```js
    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:

    ```js
    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`:
    ```js
    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:
    ```js
    deepEqual(nest(data, ['color', 'quantity']), expected);
    ```
    Nest by key functions:
    ```js
    deepEqual(nest(data, [byColor, byQuantity]), expected);
    ```
    ## Second Example
    Expected output when grouping by `type` and `color`:
    ```js
    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:
    ```js
    eq(nest(data, ['type', 'color']), expected);
    ```
    Nest by key functions:
    ```js
    eq(nest(data, [byType, byColor]), expected);
    ```
    77 changes: 77 additions & 0 deletions demo.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    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);
    13 changes: 13 additions & 0 deletions nest.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    _ = 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;