Home > Back-end >  Group nested objects with reduce
Group nested objects with reduce

Time:02-19

I have an array with objects

const data = [{
  "id": 19887003,
  "category": "Shops",
  "details": "Shoe Store",
  "star": 2,
  "subCategory": "Outlet",
},
{
  "id": 19234003,
  "category": "Shops",
  "details": "Shoe Point",
  "star": 2,
  "subCategory": "Outlet",
},
{
  "id": 190456003,
  "category": "Food",
  "details": "Korean",
  "star": 4,
  "subCategory": "Restaurant",
},
{
  "id": 190111003,
  "category": "Food",
  "details": "Chinese",
  "star": 4,
  "subCategory": "Restaurant",
},
{ 
  "id": 1902303,
  "category": "Food",
  "details": "Lounge",
  "star": 4,
  "subCategory": "Bar",
}]

this is a small piece but the structure is the same for all objects: i have a category, with multiple subcategories and sometimes the subcategory has details..for example the category food has the subcategory restourant and restourant has many types (chinese, korean). My goal is to get a structure like that:

[
   { 
      "category": "Food",
      "subCategories": [
       {
        "Subcategory": "Bar",
        "details": [
           { 
            name: "Lounge",
            star: 2,
            id: 1902303
           }
        ]
       },
       {
        "Subcategory": "Restaurant",
        "details": [
           { 
            name: "Chinese",
            star: 4,
            id: 190111003
           },
           { 
            name: "Korean",
            star: 4,
            id: 190456003
           }
        ]
       }
    },
    { 
      "category": "Shops",
      "subCategories": [
       {
        "Subcategory": "Outlet",
        "details": [
           { 
            name: "Shoe Store",
            star: 2,
            id: 19887003
           },
           { 
            name: "Shoe Point",
            star: 2,
            id: 19234003
           }
          ]
         }
       ]
    }
]

My attempt:

const groupedCategories = data.reduce((accumulator, element) => {
  const detail = element.details;
  const category = element.category;
  const subCategory = element.subCategory;
  
    if (accumulator[category]){

    return {
      ...accumulator,
      [category]: {
        ...accumulator[category],
        subCategories: [...new Set([...accumulator[category].subCategories,subCategory])],
      }
    }}
  else {

   return {
    ...accumulator,
    [category]: {
      subCategories: [subCategory],
    }
  }
  }
  
}, {});

I tried use reduce method like that but this is not the exact structure I desire in particular how to put details fields into subcategories. Thanks

CodePudding user response:

array.reduce seems to be the right choice. Simplest approach is to have double if statement to check if previous element (category and subcategory exists) and either push into existing array or create new object on upper level:

const data = [{
  "id": 19887003,
  "category": "Shops",
  "details": "Shoe Store",
  "star": 2,
  "subCategory": "Outlet",
},
{
  "id": 19234003,
  "category": "Shops",
  "details": "Shoe Point",
  "star": 2,
  "subCategory": "Outlet",
},
{
  "id": 190456003,
  "category": "Food",
  "details": "Korean",
  "star": 4,
  "subCategory": "Restaurant",
},
{
  "id": 190111003,
  "category": "Food",
  "details": "Chinese",
  "star": 4,
  "subCategory": "Restaurant",
},
{ 
  "id": 1902303,
  "category": "Food",
  "details": "Lounge",
  "star": 4,
  "subCategory": "Bar",
}]

let output = data.reduce((acc,cur) => {
    let {category, subCategory, ...rest} = cur;
    let prevCat = acc.find(x => x.category === category);
    if(!prevCat){
        acc.push({category, subCategories: [{subCategory, details: [rest]}]});
    } else {
        let prevSubCat = prevCat.subCategories.find(x => x.subCategory === subCategory);
        if(!prevSubCat) {
            prevCat.subCategories.push({subCategory, details: [rest]});
        } else {
            prevSubCat.details.push(rest);
        }
    }
    return acc;
}, []);

console.log(output);

  • Related