Search code examples
javascriptjsonlodash

Group multiple fields in JSON data


I am trying to transform a JSON file that comes from an API with a structure similar to this:

{
"fruitType": {"name": "bananas"},
"plantedIn": {"country": "USA", "state": "Minnesota"},
"harvestMethod": {"name": "manual"},
"product": {"id": "841023781723"},
},
... (other fruits, or the same fruit planted/harvested differently)

Into something like this:

"bananas": {
    "USA": {
        "manual": {
            "841023781723": {
                "fruitType": {"name": "bananas"},
                "plantedIn": {"country": "USA", "state": "Minnesota"},
                "harvestMethod": {"name": "manual"},
                "product": {"id": "841023781723"},
            }
        }
    }
},
...

So, essentially I want to group first by fruitType name, then by plantedIn country, then by harvestMethod name, and finally product id.

After some research I found that lodash is popular for this kind of grouping. I developed a very naive solution with their chain, groupBy and mapValues methods like so:

const _ = require('lodash');
const groupedData = _.chain(data)
                    .groupBy('fruitType.name')
                    .mapValues(values => _.chain(values)
                        .groupBy('plantedIn.country')
                        .mapValues(values => _.chain(values)
                            .groupBy('harvestMethod.name')
                            .mapValues(values => _.chain(values)
                                .groupBy('product.id')
                                .value()
                            ).value()
                        ).value()
                    ).value()

This solution, however functional, feels very verbose and is likely inefficient. Therefore I would like to ask if there is any better alternative, either with loadash or any other way.


Solution

  • You could take an array of function to get the keys and build the structure.

    const
        data = [{ fruitType: { name: "bananas" }, plantedIn: { country: "USA", state: "Minnesota" }, harvestMethod: { name: "manual" }, product: { id: "841023781723" } }],
        keys = [o => o.fruitType.name, o => o.plantedIn.country, o => o.harvestMethod.name, o => o.product.id],
        result = data.reduce((r, o) => {
            keys.reduce(
                (q, fn, i, { length }) => q[fn(o)] ??= i + 1 === length ? o : {},
                r
            );
            return r;
        }, {});
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }