Home > Blockchain >  How to perform multiple nested Lodash GroupBys on a tree like structure?
How to perform multiple nested Lodash GroupBys on a tree like structure?

Time:11-05

Say I have a data structure like so.

 child: [
    {
      typeOfPackage: 'subSub',
      parents: '/Test123/Diet/',
      itemName: '250 ML',
      pricePerItem: 150,
      quantity: 0,
      quantityType: '123',
      description: '5',
      avgTimeTaken: 0,
      images: [],
      isEnabled: true,
      inventory: [],
      equipment: [],
      _id: 617f9efdf0347931684888fd
    },
    {
      typeOfPackage: 'sub',
      parents: '/Test123/',
      itemName: 'Regular',
      pricePerItem: 0,
      quantity: 0,
      quantityType: '1',
      description: '1',
      avgTimeTaken: 1,
      images: [],
      isEnabled: true,
      inventory: [],
      equipment: [],
      _id: 617f9efdf0347931684888fe
    },
    {
      typeOfPackage: 'subSub',
      parents: '/Test123/Reg3/',
      itemName: '500ML',
      pricePerItem: 123,
      quantity: 0,
      quantityType: '12',
      description: '123',
      avgTimeTaken: 51,
      images: [],
      isEnabled: true,
      inventory: [],
      equipment: [],
      _id: 617f9efdf0347931684888ff
    }
  ]

I intend to transform this data by splitting parents. And my intended result looks as follows:

child: [
{
 itemName: 'Test123',
 subPackages: [
  {
    itemName: 'Diet',
    subSubPackages: [{
      typeOfPackage: 'subSub',
      parents: '/Test123/Diet/',
      itemName: '250 ML',
      pricePerItem: 150,
      quantity: 0,
      quantityType: '123',
      description: '5',
      avgTimeTaken: 0,
      images: [],
      isEnabled: true,
      inventory: [],
      equipment: [],
    }]
  },
  {
    itemName: 'Regular',
    typeOfPackage: 'sub',
    parents: '/Test123/',
    pricePerItem: 0,
    quantity: 0,
    quantityType: '1',
    description: '1',
    avgTimeTaken: 1,
    images: [],
    isEnabled: true,
    inventory: [],
    equipment: [],
    subSubPackages: [],
  },
  {
    itemName: 'Reg3',
    subSubPackages: [
    {
      typeOfPackage: 'subSub',
      parents: '/Test123/Reg3/',
      itemName: '500ML',
      pricePerItem: 123,
      quantity: 0,
      quantityType: '12',
      description: '123',
      avgTimeTaken: 51,
      images: [],
      isEnabled: true,
      inventory: [],
      equipment: [],
      _id: 617f9efdf0347931684888ff
    }
   ]
  },
 ]
}
]

I tried using lodash's chain and groupBy but I could only get as far as grouping it by the first itemName (Test123). I could not figure out how to do further grouping inside that without using a custom for loop and map methods and that too confused me.

CodePudding user response:

You could split parents and build a nested structure.

This approach takes an shadow object for a faster access to same named parents and returns only the payload without organizing structure.

If you like to use subPackages or subSubPackages, you could take a function for generating this key along with the actuyl nesting level. For later processing data, I recommend to use only generic names, like children for every level.

const
    getSub = level => `sub${'Sub'.repeat(level)}Level`,
    data = [{ typeOfPackage: 'subSub', parents: '/Test123/Diet/', itemName: '250 ML', pricePerItem: 150, quantity: 0, quantityType: '123', description: '5', avgTimeTaken: 0, images: [], isEnabled: true, inventory: [], equipment: [], _id: '617f9efdf0347931684888fd' }, { typeOfPackage: 'sub', parents: '/Test123/', itemName: 'Regular', pricePerItem: 0, quantity: 0, quantityType: '1', description: '1', avgTimeTaken: 1, images: [], isEnabled: true, inventory: [], equipment: [], _id: '617f9efdf0347931684888fe' }, { typeOfPackage: 'subSub', parents: '/Test123/Reg3/', itemName: '500ML', pricePerItem: 123, quantity: 0, quantityType: '12', description: '123', avgTimeTaken: 51, images: [], isEnabled: true, inventory: [], equipment: [], _id: '617f9efdf0347931684888ff' }],
    result = data
        .reduce((r, o) => {
            o
                .parents
                .split('/')
                .filter(Boolean)
                .reduce((t, itemName, i) => {                            
                    if (!t[itemName]) {
                        t[itemName] = { _: [] };
                        t._.push({ itemName, [getSub(i)]: t[itemName]._ });
                    }
                    return t[itemName];
                }, r)
                ._
                .push(o);
            return r;
        }, { _: [] })
        ._;

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related