I am trying to determine the best data-type to sort an array of objects, into groups defined by properties. I have an array of objects, like so:
var 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
}
];
I need to group these objects into an arbitrary-defined group. E.g.:
gender
age
(This can be defined by the server and can change to contain a third group if we wish.)
Which then gives me (visually):
Male:
21:
Gary
22:
Pete
Frank
Female
20:
Samantha
Maria
21:
Hannah
I think the appropriate data type would be an object of objects. I.e.:
{
Male: {
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
}
]
}
}
}
But I cannot work out, for the life of me, an appropriate algorithm to sort these objects into a data-type which represents the above.
There is a useful utility in underscore.js
called _.groupBy(arr, callback)
which I can use as follows:
_.groupBy(people, function (person) {
var props = ['gender', 'age'], // server-defined
prop = [];
for (var i = 0, length = props.length; i < length; i++) {
prop.push(person[props[i]]);
}
return prop.join('|');
});
This gives me a 1-depth object which I can use a for...in
loop to iterate over the keys, and loop through to build the object above, but it's that bit of the code I'm stuck on.
The returned object would be:
{
"Male|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
}
],
"Male|21": [
{
"name": "Gary",
"gender": "Male",
"age": 21
}
],
"Female|21": [
{
"name": "Hannah",
"gender": "Female",
"age": 21
}
],
"Male|20": [
{
"name": "Pete",
"gender": "Male",
"age": 20
}
]
}
I'm thinking of then looping through each key in the object, splitting at the pipe (|
) and using recursion to construct a new object of objects containing the groups/array of data.
That's such a horrendous way of achieving this but I have no idea how to do it otherwise.
Is there a better way I'm missing?
Maybe this helps you. It utilises an array with the properties of the object and the result is grouped by the content of the properties.
The forEach
loop iterates over the data. The reduce
loop is for generating grouped properties for every given group and if it is the last one it returns an array, if not already there.
The last step is to push the value of one of people to the array.
var 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'],
grouped = {};
people.forEach(function (a) {
groups.reduce(function (o, g, i) { // take existing object,
o[a[g]] = o[a[g]] || (i + 1 === groups.length ? [] : {}); // or generate new obj, or
return o[a[g]]; // at last, then an array
}, grouped).push(a);
});
document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');
you can use lodash
groupBy(people, (item) => item.age + item.gender);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With