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.
CodePudding user response:
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; }
CodePudding user response:
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;
}, {});