13

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!

3
  • What determines the final ordering? First label encountered? Commented Sep 8, 2020 at 5:17
  • Do you want the id values to start at 1 for each label? That's not what most of the answers do... Commented Sep 8, 2020 at 5:34
  • @Nick apologies that was a typo, the ids should have been be 3 and 4 not 1 and 2 again like I had in my original post. Thats my mistake! Commented Sep 8, 2020 at 5:37

7 Answers 7

11

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

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

Comments

2

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)

Comments

2

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

4 Comments

Nice pickup on the IDs but probably not what OP wants 😉
Yeah, thought the id of the expected answer might just be a typo.
@Phil yeah, as I say, this will produce the expected output from the question but it does seem that isn't what they actually want. I've requested clarification to be sure...
Hi all yes sorry that was a typo, I fixed it in my question. I don't need each options array to start with 1. Apologies :) Thank you all for leading me in the right direction!
2

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)

4 Comments

your ids are 1 ,2 and 3,4 bt he wants it like 1,2 and 1,2
@Ifaruki I'd be very surprised if that's what OP actually wants (given the answer they've already accepted)
well he should then be more precise
sorry all! was a typo. thats on me! should have been 1,2,3,4 originally. Thank you for your help
2

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>

1 Comment

That's nice... really compact.
1

We can make use of Object.groupBy as the first step to aggregate the objects by the grouping key for a much simplified solution.

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 res = Object.entries(Object.groupBy(tech, o => o.grouping))
  .map(([label, v]) => ({label, options: 
    v.map(({grouping, ...rest}) => rest)}));
console.log(res);

Comments

0
const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 5 },
  { name: "bananas", type: "fruit", quantity: 10 },
  { name: "goat", type: "meat", quantity: 40 },
  { name: "cherries", type: "fruit", quantity: 5 },
  { name: "fish", type: "meat", quantity: 22 },
  { name: "fish", type: "cereals", quantity: 100 },
  { name: "fish", type: "meat", quantity: 20 },
  { name: "fish", type: "cereals", quantity: 12 },
];
function func(inventory){
  let type = [], result = {};
  inventory.map((item)=>{type.push(item.type)})
  type = [...new Set(type)]
  for(i=0;i<inventory.length;i++){
   if(type.find(item => item === inventory[i].type)){
       result= {
        ...result,
        [inventory[i].type]: 
        result[type.find(item => item === inventory[i].type)] ? 
        result[type.find(item => item === inventory[i].type)] +inventory[i].quantity
        : inventory[i].quantity


      }
    }
  }
  return result
 }

console.log(func(inventory))

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

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.