First, we can take the data you are starting with and create a new array of objects where we map each individual to their assigned age, and then we sort that array of objects by its age
property value:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const associateUserDetails = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age);
console.log(associateUserDetails(users));
/* -> [
{ name: "joe", age: 20 },
{ name: "bob", age: 21 },
{ name: "tim", age: 22 },
{ name: "joe", age: 23 }
]
*/
Once we this array of users sorted by their age, all that's left to do is split the array of objects back up into two nested arrays for name
and age
, like this:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const associateUserDetails = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age);
const splitByDetail = users => users.flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {});
console.log(splitByDetail(associateUserDetails(users)));
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23]
}
*/
Now let's combine this all together:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const sortUsersByAge = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age).flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {});
console.log(sortUsersByAge(users));
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23]
}
*/
BONUS
Now for a bonus— let's say you have an object like the one you provided and…
- are working with many more user details, maybe even a variable number of details, and you would rather list them all explicitly
- want the ability to sort by any of the user details listed, name (alphabetical sorting), age (numerical sorting), ID number (numerical sorting), or some other property (default to alphabetical sorting unless all values are numbers)
- would like to add a boolean true/false switch to quickly reverse the order of any sorting function like
sortUsers(users, true)
where the second parameter would reverse each final array's state after sorting
- you want to use this function as an
Object.prototype
method, like users.sortBy('age')
instead of sortObject(users, 'age')
Let's make all of those improvements and put it all together into one prototype function Object.sortBy()
:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20],
color: ["blue", "green", "red", "yellow"]
};
Object.prototype.sortBy = function(sortBy, descending = false) { return sortBy && Object.keys(this).includes(sortBy) ? [Array(this[Object.keys(this)[0]].length).fill().map((e,i) => Object.fromEntries(Object.keys(this).map(key => [key, this[key][i]])))].map(obj => obj.sort((a,b) => obj.every(user => typeof user[sortBy] === "number") ? a[sortBy] - b[sortBy] : (a[sortBy] > b[sortBy] ? 1 : -1)))[0].flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {}) : this };
users.sortBy('age');
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23],
color: ["yellow", "red", "green", "blue"]
}
*/
users.sortBy('name');
/* -> {
name: ["bob", "joe", "joe", "tim"],
age: [21, 20, 23, 22],
color: ["red", "yellow", "blue", "green"]
}
*/
users.sortBy('color', true); // true here will sort colors by name in descending order
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23],
color: ["yellow", "red", "green", "blue"]
}
*/
tim
is22
and in the second,tim
is21
. Is that correct?sortBy('age')
works dynamically to take all properties into account and sort by whichever you want, in ascending order (default) or descending order (set 2nd param totrue
)