1

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.

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

4 Comments

Btw how different would it be if I wanted to wrap each group key in a array? For instance "bananas": ["USA": [...]]
at least you need an object with the keys. do you want to group with arrays?
I was just wondering if it was possible because it would simplify consuming the data in the frontend
what result do you rxpect? maybe ask a new qustion?
1

There is already an answer but here is maybe a more readable solution

const grouped = items.reduce((acumm, current, index) => {
    acumm[current.fruitType.name] = {
      [current.plantedIn.country]: {
        [current.harvestMethod.name]: {
          [current.product.id]: current,
          ...(acumm[current.fruitType.name]?. 
                [current.plantedIn.country]?. 
                [current.harvestMethod.name] ?? {}
          ),
        },
      },
    };
    return acumm;
}, {});

Stackblitz Example

1 Comment

Thank you for your solution. I will be accepting Nina's because it is more modular.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.