2

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.

1
  • Do the same thing again, in a loop, on the individual arrays. Commented Apr 1, 2020 at 12:18

3 Answers 3

3

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

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the clean & clear solution and good explanation of how it works. I have decided to use your solution, and decided to skip the groupBy function by hardcoding both team and key properties: const result = data.reduce((acc, obj) => ({...acc, [obj.team]: {...(acc[obj.team] || {}), [obj.key]: obj}}), {})
2

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

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.