marielle
marielle

Reputation: 438

Group by an array of objects by multiple keys taking care of all the keys

I've this dataset:

const people = [
    {
        name: 'Pete',
        gender: 'Male',
        age: 22

    },
    {
        name: 'Samantha',
        gender: 'Female',
        age: 20

    },
    {
        name: 'Frank',
        gender: 'Male',
        age: 22

    },
    {
        name: 'Gary',
        gender: 'Male',
        age: 21

    },
    {
        name: 'Maria',
        gender: 'Female',
        age: 20

    },
    {
        name: 'Hannah',
        gender: 'Female',
        age: 21

    },
    {
        name: 'Pete',
        gender: 'Male',
        age: 20

    }
];

and I wnt to group by gender and by age. So the result should be:

{
  Male: {
    '20': [ { name: 'Pete', gender: 'Male', age: 20 } ],
    '21': [ { name: 'Gary', gender: 'Male', age: 21 } ],
    '22': [
      { name: 'Pete', gender: 'Male', age: 22 },
      { name: 'Frank', gender: 'Male', age: 22 }
    ]
  },
  Female: {
    '20': [
      { name: 'Samantha', gender: 'Female', age: 20 },
      { name: 'Maria', gender: 'Female', age: 20 }
    ],
    '21': [ { name: 'Hannah', gender: 'Female', age: 21 } ],
    '22': [ ],
  }
}

You can see that the age keys are always 20, 21, 22 even if there aren't females of 22 years old.

I try:

  const groupedByMultiple = map(groupBy(dataset, 'gender'), function (obj, key) {
    const temp = {}
    temp[key] = groupBy(obj, 'age')
    return temp
  })

but the result is an array instead of an object. How can I solve?

Upvotes: 0

Views: 86

Answers (3)

chaimm
chaimm

Reputation: 400

you can use array.reduce to get objects from arrays. it can return anything you want from acumulating values from an array. if the keys you want are known before it is much simpler by setting initial value.

    people.reduce((accumulated, person) => {
     acccumulated[person.gender][person.age].push(person);
     return accumulated;
]}, {
 male: {
20: [],
21: [],
22: []
},
female: {
20: [],
21: [],
22: []
},
});

if you don't know what values there are you can still do it by checking for what is there and assigning appropriately

   people.reduce((accumulated, person) => {
         if(acccumulated[person.gender]) {
          const arr = acccumulated[person.gender][person.age] ? acccumulated[person.gender][person.age] : [];
           arr.push(person)
           acccumulated[person.gender][person.age] = arr;
         } else {
         acccumulated[person.gender] = {};
        acccumulated[person.gender][person.age] = [person];
    
         }
         
         return accumulated;
    ]}, {});

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386570

You could take an array of keys fro the wanted groups and reduce the group for pushing the object.

const
    people = [{ name: 'Pete', gender: 'Male', age: 22 }, { name: 'Samantha', gender: 'Female', age: 20 }, { name: 'Frank', gender: 'Male', age: 22 }, { name: 'Gary', gender: 'Male', age: 21 }, { name: 'Maria', gender: 'Female', age: 20 }, { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }],
    groups = ['gender', 'age'], result = people.reduce((r, o) => {
        groups
            .reduce((group, k, i, { length }) => group[o[k]] ??= i + 1 === length ? [] : {}, r)
            .push(o);
        return r;
    }, {});

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

Upvotes: 0

kusma
kusma

Reputation: 69

//distinct ages
let distinctAges=[];
people.foeEach(v=>{
if(distimctAges.includes(v.age)){
   distinctAges.push(v.age)
}
})


//init empty assign
let resultObj={
  Male:{},
  Female:{}
};

//assign data
distinctAges.foeEach(v=>{
resultObj.Male[v.age]=filter('Male',v.age);
resultObj.Female[v.age]=filter('Female',v.age);
})

//filter by gender and age
funtion filter(gender,age){
  return people.filter(data=>data.age==age & data.gender==gender);
}

console.log(resultObj);

Upvotes: 0

Related Questions