Mohd Sabban
Mohd Sabban

Reputation: 192

How to groupBy array of object in JavaScript

I want to groupBy my array of an object into some another desired array of objects. I go through several tutorials but didn't get the appropriate output.

What I want is based on their groupBy properties I want to group all the elements

Here is my input

permissions= [
    {
      code: 'U00',
      permission_name: 'Read User',
      groupBy: 'User',
      icon: 'user',
    },
    {
      code: 'U01',
      permission_name: 'Create User',
      groupBy: 'User',
      icon: 'user',
    },
  
    {
      code: 'B00',
      permission_name: 'Read Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B01',
      permission_name: 'Create Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B10',
      permission_name: 'Update Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
  ];

Required output

 Output = [
    {
      label: 'User',
      icon: 'user',
      children: [
        {
          label: 'Create Users',
        },
        {
          label: 'Read All Users',
        },
      
      ],
    },
    {
      label: 'Batch',
      children: [
        {
          label: 'Create Batchs',
        },
        {
          label: 'Read All Batch',
        },
        {
          label: 'Update Batch',
        },
        {
          label: 'Disabled Batch',
        },
      ],
    },
  ];

Upvotes: 0

Views: 157

Answers (6)

Spencer Bard
Spencer Bard

Reputation: 1035

[curItem.groupBy]You should try using reduce: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

return Object.values(permissions.reduce((curObj, curItem) => { 
  if(!curObj.hasOwnProperty(curItem.groupBy)){
    curObj[curItem.groupBy] = { 
      label: curItem.label, 
      icon: curItem.icon, 
      children: [] 
    };
  }
  curObj[curItem.groupBy].children.push({ label: curItem.label });
  return curObj;
}, {}));

Upvotes: 2

Yves Kipondo
Yves Kipondo

Reputation: 5603

You can use Array.prototype.reduce to alter the permissions Array the way you'll like.

let permissions = [{
      code: 'U00',
      permission_name: 'Read User',
      groupBy: 'User',
      icon: 'user',
    },
    {
      code: 'U01',
      permission_name: 'Create User',
      groupBy: 'User',
      icon: 'user',
    },
  
    {
      code: 'B00',
      permission_name: 'Read Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B01',
      permission_name: 'Create Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B10',
      permission_name: 'Update Batch',
      groupBy: 'Batch',
      icon: 'user',
    }
];
    
let results = permissions.reduce(function(accumulator, current) {
    let previousPermission = accumulator.find(function(el){
        return el.label === current.groupBy
    });
    
    if(!previousPermission){
        return accumulator.concat({
            label: current.groupBy, 
            icon: current.icon,
            children: [{
              label: current.permission_name  
            }]
        });
    } else {

        let permissionIndex = accumulator.findIndex(item => {
          return item.label === previousPermission.label;     
        });

        previousPermission = {
            ...previousPermission,
            children: [
              ...previousPermission.children,
              { label: current.permission_name }
            ]
        }
        accumulator[permissionIndex] = previousPermission;
        return accumulator;
    }
  
}, []);

console.log(results);

Upvotes: 0

KobraKalle
KobraKalle

Reputation: 31

You could first create a function to reduce the array into an object, like so:

const groupBy = (prop, list) => {
    return list.reduce((groups, item) => {
        const propVal = item[prop];
        const target = groups[propVal] ?? [];
        
        return { ...groups, [propVal]: [...target, item] };
    }, {});
};

Or shorter:

const groupBy = (prop, list) => list.reduce((groups, item) => ({ ...groups, [item[prop]]: [...groups[item[prop]] ?? [], item] }), {});

And then:

const permObj = groupBy('groiupBy', permissions);
// { User: [...], Batch: [...] }

Now you could just work with that object or continue by writing a second function:

const groupsToArray = (nameProp, groups) => Object.entries(groups).map((g) => ({ [nameProp]: g[0], data: g[1] }))

And use it:

const newPermList = groupsToArray('label', permObj);

which would leave you with an array of objects with the according labels and the children in the 'data' prop of each object.

Of course then you could still filter out or map to get rid of duplicate data that you dont need. But you get the idea.

Upvotes: 1

Tigran Abrahamyan
Tigran Abrahamyan

Reputation: 776

First step - get filtered necessary groupBy values.

Second step - mapping through filtered groupBy values and create a new array of objects based on your requirements.

const permissions = [
  {
    code: 'U00',
    permission_name: 'Read User',
    groupBy: 'User',
    icon: 'user',
  },
  {
    code: 'U01',
    permission_name: 'Create User',
    groupBy: 'User',
    icon: 'user',
  },

  {
    code: 'B00',
    permission_name: 'Read Batch',
    groupBy: 'Batch',
    icon: 'user',
  },
  {
    code: 'B01',
    permission_name: 'Create Batch',
    groupBy: 'Batch',
    icon: 'user',
  },
  {
    code: 'B10',
    permission_name: 'Update Batch',
    groupBy: 'Batch',
    icon: 'user',
  },
];

const groups = [ ...new Set(permissions.map(({ groupBy }) => groupBy)) ];

const result = groups.map(groupName => ({
  value: permissions.find(permission => permission.groupBy === groupName).groupBy,
  icon: permissions.find(permission => permission.icon === groupName.toLowerCase())?.icon,
  children: permissions
    .filter(permission => permission.groupBy === groupName)
    .map(n => ({ value: n.permission_name })),
}));

const output = JSON.parse(JSON.stringify(result)); // For removing undefined keys.

console.log(output);

Upvotes: 0

Giovanni Esposito
Giovanni Esposito

Reputation: 11156

You colud find distinct groupBy values and then iterate on this array to fill result array like:

let permissions= [
    {
      code: 'U00',
      permission_name: 'Read User',
      groupBy: 'User',
      icon: 'user',
    },
    {
      code: 'U01',
      permission_name: 'Create User',
      groupBy: 'User',
      icon: 'user',
    },
  
    {
      code: 'B00',
      permission_name: 'Read Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B01',
      permission_name: 'Create Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
    {
      code: 'B10',
      permission_name: 'Update Batch',
      groupBy: 'Batch',
      icon: 'user',
    },
  ];
  
  let groups = [...new Set(permissions.map(({groupBy})=>groupBy))];
  let result = [];
  for (let i = 0; i < groups.length; i++){
     let res = {};
     res.label = groups[i];
     res.icon = permissions.filter(x => x.groupBy === groups[i])[0].icon;
     res.children = [];
     let arrayOfGroup = permissions.filter(x => x.groupBy === groups[i]);
     for (let j = 0; j < arrayOfGroup.length; j ++){
        let child = {};
        child.label = arrayOfGroup[j].permission_name;
        res.children.push(child);
     }
     result.push(res);
  }
  console.log(result);
  

Upvotes: 0

Kira
Kira

Reputation: 92

You can write something like:

const output = []

permissions.forEach(obj => {
  const dest = output.find(target => target.label === obj.groupBy);

  if (!dest) {
    output.push({ label: obj.groupBy, icon: obj.icon, children: [ { label: obj.permission_name } ] })
  } else {
    dest.children.push({ label: obj.permission_name })
  }
})

You are basically creating a new array, mapping the original based on your requires.

Upvotes: 1

Related Questions