简体   繁体   中英

Javascript - group array of objects by common values with label

I am trying to turn an array of objects into another array of objects by grouping by a specific value and adding that value as label and taking it out of the object in the new array.

Input: So for instance I have this array of objects:

let tech = [
  { id: 1, grouping: "Front End", value: "HTML" },
  { id: 2, grouping: "Front End", value: "React" },
  { id: 3, grouping: "Back End", value: "Node" },
  { id: 4, grouping: "Back End", value: "PHP" },
];

Expected: I am looking to try and figure out how I can get to this, where there is a label for each of the unique groupings and options array containing the values of that grouping.

[
  {
    label: "Front End",
    options: [
      { id: 1, value: "HTML" },
      { id: 2, value: "React" },
    ],
  },
  {
    label: "Back End",
    options: [
      { id: 3, value: "Node" },
      { id: 4, value: "PHP" },
    ],
  },
]

The closest I have been able to get to is using reduce to group by the grouping key:

const groupedTech = tech.reduce((acc, value) => {
  // Group initialization
  if (!acc[value.grouping]) {
    acc[value.grouping] = [];
  }
 
  // Grouping
  acc[value.grouping].push(value);
 
  return acc;
}, {});

Which gives me this:

{
  "Front End": [
    { id: 1, grouping: "Front End", value: "HTML" },
    { id: 2, grouping: "Front End", value: "React" },
  ],
  "Back End": [
    { id: 3, grouping: "Back End", value: "Node" },
    { id: 4, grouping: "Back End", value: "PHP" },
  ],
}

But this returns object not an array and doesn't remove the grouping value. I have not been able to figure out how to group properly because in the array of objects I have not found an efficient way to compare against to see if the grouping exists and if so add to that nested array. Would I be better off using something like .map() ? Appreciate any leads/learnings!

You're very close, just wrap the key-value entries of the result you've got in a map function:

 let tech = [ { id: 1, grouping: "Front End", value: "HTML" }, { id: 2, grouping: "Front End", value: "React" }, { id: 3, grouping: "Back End", value: "Node" }, { id: 4, grouping: "Back End", value: "PHP" }, ]; const groupedTech = Object.entries( // What you have done tech.reduce((acc, { id, grouping, value }) => { // Group initialization if (!acc[grouping]) { acc[grouping] = []; } // Grouping // FIX: only pushing the object that contains id and value acc[grouping].push({ id, value }); return acc; }, {}) ).map(([label, options]) => ({ label, options })); console.log(groupedTech);

You just have to do one more manipulation with Object.entries and .map

 let tech = [ { id: 1, grouping: 'Front End', value: 'HTML' }, { id: 2, grouping: 'Front End', value: 'React' }, { id: 3, grouping: 'Back End', value: 'Node' }, { id: 4, grouping: 'Back End', value: 'PHP' } ] const groupedTech = tech.reduce((acc, value) => { // Group initialization if (!acc[value.grouping]) { acc[value.grouping] = [] } // Grouping acc[value.grouping].push(value) return acc }, {}) const res = Object.entries(groupedTech).map(([label, options]) => ({ label, options })) console.log(res)

A minor variation on the other two answers if you want to get exactly the output you specify:

 let tech = [{ id: 1, grouping: "Front End", value: "HTML" }, { id: 2, grouping: "Front End", value: "React" }, { id: 3, grouping: "Back End", value: "Node" }, { id: 4, grouping: "Back End", value: "PHP" }, ]; const groupedTech = Object.entries( tech.reduce((acc, value) => { // Group initialization if (!acc[value.grouping]) { acc[value.grouping] = []; } // Grouping acc[value.grouping].push({ id: acc[value.grouping].length+1, value: value.value }); return acc; }, {})) .map(([label, options]) => ({ label, options })); console.log(groupedTech);

I usually like to build up a Map of key / value pairs then transform those entries into the final result (usually using Array.prototype.map() or Array.from() ).

 const tech = [ { id: 1, grouping: "Front End", value: "HTML" }, { id: 2, grouping: "Front End", value: "React" }, { id: 3, grouping: "Back End", value: "Node" }, { id: 4, grouping: "Back End", value: "PHP" }, ]; const groupedMap = tech.reduce((map, { grouping, ...option }) => { if (!map.has(grouping)) { map.set(grouping, []) } map.get(grouping).push(option) return map }, new Map()) const groupedTech = Array.from(groupedMap, ([ label, options ]) => ({ label, options })) console.log(groupedTech)

Using a Map and Map#values()

 const grouped = tech.reduce((m,{grouping:label, ...rest})=>{ const group = m.get(label) || {label, options:[]}; group.options.push({...rest}) return m.set(label, group) },new Map) console.log([...grouped.values()])
 <script> let tech=[{id:1,grouping:"Front End",value:"HTML"},{id:2,grouping:"Front End",value:"React"},{id:3,grouping:"Back End",value:"Node"},{id:4,grouping:"Back End",value:"PHP"}]; </script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM