I have a set of objects (product data) that I have sorted from an API - the objects contain repeat keys and repeat arrays of information as a product can have many categories and subcategories.
{
"category":"e-liquid",
"subcategories":[
{
"attributes":{
"name":"50ml",
"id":19
}
},
{
"attributes":{
"name":"100ml",
"id":18
}
},
],
}
{
"category":"e-liquid",
"subcategories":[
{
"attributes":{
"name":"50ml",
"id":19
}
},
{
"attributes":{
"name":"100ml",
"id":18
}
},
],
}
{
"category":"e-liquid",
"subcategories":[
{
"attributes":{
"name":"50ml",
"id":19
}
}
]
}
{
"category":"hardware",
"subcategories":[
{
"attributes":"tanks",
"id":15
}
]
}
{
"category":"hardware",
"subcategories":[
{
"attributes":"tanks",
"id":15
},
{
"attributes":"coils",
"id":14
}
]
}
Each JSON object above represents an individual product.
I want to be able to merge/reduce all subcategories uniquely by their category key i.e. e-liquid, hardware or whatever else gets thrown at it into a singular flat object or array, one for each category I suppose. Something like:
{
"category":"e-liquid",
"subcategories":[
"50ml",
"100ml",
"150ml",
"200ml",
"...anything else"
]
}
{
"category": "hardware",
"subcategories":[
"coils",
"tanks",
"batteries",
"...whatever else"
]
}
Any insight is appreciated. Been searching stackoverflow for a while but nothing seems to have cropped up - already tried a few solutions from similarly worded questions, but often merges were too shallow, and I can't wrap my head around how I would deep merge (assuming that's what's needed here). My lodash isn't working in my nuxt config hence why I specifically am asking for non-lodash solutions if possible.
CodePudding user response:
We can group the objects by category by building an object with categories at its keys. The simplification of subcategories is a little trickier, since the structure of subcategory objects differs by category.
In the snippet below, I've introduced subCatValue
, an object whose values are functions that specify how to get the salient subcategory value for a given category.
The reduce
groups by category and does the subcategory combination in one trip through the array. A second trip cleans up duplicate subcategories.
const data = theAPIdata();
const subCatValue = {
'e-liquid': obj => obj.attributes.name,
'hardware': obj => obj.attributes
}
const index = data.reduce((acc, product) => {
acc[product.category] ??= { category: product.category, subcategories: [] };
// subCatValue[product.category] is a function that plucks the value we need
// mapping it over the subcategory array produces an array of salient values
const values = product.subcategories.map(subCatValue[product.category]);
acc[product.category].subcategories.push(...values)
return acc;
}, {});
const values = Object.values(index);
// go through again, removing duplicate subcategories
values.forEach(v => {
v.subcategories = Array.from(new Set(v.subcategories))
});
console.log(values);
function theAPIdata() {
return [{
"category": "e-liquid",
"subcategories": [{
"attributes": {
"name": "50ml",
"id": 19
}
},
{
"attributes": {
"name": "100ml",
"id": 18
}
},
],
},
{
"category": "e-liquid",
"subcategories": [{
"attributes": {
"name": "50ml",
"id": 19
}
},
{
"attributes": {
"name": "100ml",
"id": 18
}
},
],
},
{
"category": "e-liquid",
"subcategories": [{
"attributes": {
"name": "50ml",
"id": 19
}
}]
},
{
"category": "hardware",
"subcategories": [{
"attributes": "tanks",
"id": 15
}]
},
{
"category": "hardware",
"subcategories": [{
"attributes": "tanks",
"id": 15
},
{
"attributes": "coils",
"id": 14
}
]
}
];
}
CodePudding user response:
Try it:
Learn more about Set()
const obj = [{ "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } }, { "attributes":{ "name":"100ml", "id":18 } }, ], }, { "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } }, { "attributes":{ "name":"100ml", "id":18 } }, ], }, { "category":"e-liquid", "subcategories":[ { "attributes":{ "name":"50ml", "id":19 } } ] }, { "category":"hardware", "subcategories":[ { "attributes":"tanks", "id":15 } ] }, { "category":"hardware", "subcategories":[ { "attributes":"tanks", "id":15 }, { "attributes":"coils", "id":14 }]}];
const subCat = item => item.map(it => it.attributes.name || it.attributes);
const res = obj.reduce((a, {category, subcategories}) => {
if (a[category]) return {...a, [category]: {category, subcategories: [...new Set([...a[category].subcategories, ...subCat(subcategories)])]}}
return {...a, [category]: {category, subcategories: [...new Set([...subCat(subcategories)])]}};
},{});
console.log(Object.values(res));
CodePudding user response:
Get the unique category names, then get unique attribute names for each of those categories:
const data = [{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}},{"attributes":{"name":"100ml","id":18}}]},{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}},{"attributes":{"name":"100ml","id":18}}]},{"category":"e-liquid","subcategories":[{"attributes":{"name":"50ml","id":19}}]},{"category":"hardware","subcategories":[{"attributes":"tanks","id":15}]},{"category":"hardware","subcategories":[{"attributes":"tanks","id":15},{"attributes":"coils","id":14}]}];
const r = [...new Set(data.map(i=>i.category))].map(i=>({
category:i,
subcategories:[[...new Set(data.filter(({category:c})=>c===i)
.flatMap(({subcategories:s})=>s.map(({attributes:a})=>a.name??a)))]]
}));
console.log(r);