JavaScript ES6: array of objects to array grouped by key and indexed by property

Viewed 317

I have an array of objects. The simplified version (with just a few of the properties):

let d = [
{ team:"sales", key:"employees", value:24 },
{ team:"sales", key:"floor", value:2 },
{ team:"finance", key:"employees", value:7 },
{ team:"finance", key:"floor", value:2 },
]

Want to group by team and then by key, using plain (ES6) JavaScript:

{
  sales: {
    employees: { team:"sales", key:"employees", value:24 },
    floor: { team:"sales", key:"floor", value:2 }
  },
  finance: {
    employees: { team:"finance", key:"employees", value:7 },
    floor: { team:"finance", key:"floor", value:2 }
  }
}

Somewhat similar to other questions, but not quite.

What I have so far: using reduce to group by team:

let groupBy = (data, field) => data.reduce((acc, obj) => Object.assign(acc, { [obj[field]]:( acc[obj[field]] || [] ).concat(obj) }), {})

let result = groupBy(d,'team')

This gives:

{
sales: [
  { team:"sales", key:"employees", value:24 },
  { team:"sales", key:"floor", value:2 }
],
finance: [
  { team:"finance", key:"employees", value:7 },
  { team:"finance", key:"floor", value:2 }
]
}

Not sure what the recommended way is to group by the key level inside the teams.

3 Answers

Instead of accumulating to an array by concatenating each object to a new array, you can accumulate to a new object literal. You can do this by spreading the accumulated object into a new object literal. Then, again, you can spread the result of the accumulated object for the current objects team or take a default object (|| {}) if one doesn't exist. Lastly, you can set a new key on your nested object literal of obj.key and sets its value to be the current iterated object itself.

const data = [
  { team:"sales", key:"employees", value:24 },
  { team:"sales", key:"floor", value:2 },
  { team:"finance", key:"employees", value:7 },
  { team:"finance", key:"floor", value:2 },
];

const groupBy = (array, field) => array.reduce((acc, obj) =>
  ({...acc, [obj[field]]: {...(acc[obj[field]] || {}), [obj.key]: obj}}), {});

console.log(groupBy(data,'team'));

You could take an array of keys for grouping and reduce the object by checking and adding new objects or for the last key take the object from the data.

let d = [{ team: "sales", key: "employees", value: 24 }, { team: "sales", key:" floor", value: 2 }, { team: "finance", key: "employees", value: 7 }, { team: "finance", key: "floor", value: 2 }],
    groups = ['team', 'key'],
    result = d.reduce((acc, obj) => {
        groups.reduce((o, key, i, { length }) => {
             const k = obj[key];
             return o[k] = o[k] || (i + 1 === length ? obj : {});
        }, acc);
        return acc;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can use reduce

let d = [{ team:"sales", key:"employees", value:24 },{ team:"sales", key:"floor", value:2 },{ team:"finance", key:"employees", value:7 },{ team:"finance", key:"floor", value:2 },]

let groupBy = (data, property) => data.reduce((op, inp) => {
  let prop = inp[property]
  let key = inp.key
  op[prop] = op[prop] || {}
  op[prop][key] = op[prop][key] || {}
  op[prop][key] = inp
  return op
}, {})


console.log(groupBy(d, 'team'))

Related