Home > Software design >  Merge a nested array of objects by overall key (without lodash)
Merge a nested array of objects by overall key (without lodash)

Time:11-22

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

  • Related