I'm trying to "enhance" my sorting function for accepting multiple criteria and so doing multi sorting of my array of objects.
Sorry if this is a simple question, or if I committed "newbie" mistakes, but I'm a Frontend developer learning JS foundamentals, and I'm trying to learn how sorting functions working.
Here's my code:
const arrayOfObjects = [
{
name: 'John',
age: 30,
city: 'New York',
details: {
occupation: 'developer',
}
},
{
name: 'Jane',
age: 25,
city: 'Paris',
details : {
occupation: 'designer',
}
}
];
// This is my main sorting function that I'd like to use for "multi sorting" an array of objects
function sortByKey(criteria) {
return (a, b) => {
let comparison = 0;
criteria.forEach(criterion => {
const varA = (typeof resolvePath(criterion.key, a) === 'string')
? resolvePath(criterion.key, a).toUpperCase() : resolvePath(criterion.key, a);
const varB = (typeof resolvePath(criterion.key, b) === 'string')
? resolvePath(criterion.key, b).toUpperCase() : resolvePath(criterion.key, b);
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
if(criterion.order === 'desc') {
comparison = (comparison * -1)
}
});
return comparison;
};
}
// This function is used to resolve the path of a key in an object
function resolvePath(path, obj) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : null
}, obj || self)
}
The usage of this is:
// MAIN USAGE
console.log( arrayOfObjects.sort(sortByKey([ { key: 'age' } ])) )
console.log( arrayOfObjects.sort(sortByKey([ { key: 'details.occupation', order: 'desc' } ])) )
But I would like to use it some kind of:
console.log( arrayOfObjects.sort(sortByKey([ { key: 'details.occupation' }, { key: 'age', order: 'desc' ])) )
This is because I want to give users the possibility to multi-sort my array in some kind of order, like "Age first, name second, etc..."
Thanks for any useful help, have a nice day!
CodePudding user response:
Create a function which returns the comparator value for each criteria. Loop through the array and call the compare function UNTIL a non-zero value is found. find
will stop looking when compareByKey
returns 1
or -1
.
const sortByKey = (criteria) => (a, b) => {
let returnValue = 0;
criteria.find(c => returnValue = compareByKey(c, a, b));
return returnValue;
}
function compareByKey({ key, order = 'asc' }, a, b) {
const varA = resolvePath(key, a)
const varB = resolvePath(key, b)
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
if (order === 'desc') {
comparison *= -1
}
return comparison
}
If the find
part is confusing or ugly:
Another way would be to reduce
the comparator value of each property. But, this doesn't short circuit when a non-zero value is found.
const sortByKey = criteria =>
(a, b) =>
criteria.reduce((acc,c) => acc || compareByKey(c, a, b), 0)
or
const sortByKey = (criteria) => (a, b) => {
let comparedValue = 0;
for (const c of criteria) {
comparedValue = compareByKey(c, a, b);
if (comparedValue)
return comparedValue;
}
return comparedValue;
}
const arrayOfObjects = [{
name: 'John',
age: 30,
city: 'New York',
details: {
occupation: 'developer',
}
},
{
name: 'Jane',
age: 25,
city: 'Paris',
details: {
occupation: 'designer',
}
},
// added another designer for testing
{
name: 'Jane 2',
age: 30,
city: 'Paris',
details: {
occupation: 'designer',
}
}
];
const sortByKey = (criteria) => (a, b) => {
let returnValue = 0;
criteria.find(c => returnValue = compareByKey(c, a, b));
return returnValue;
}
function compareByKey({ key, order = 'asc' }, a, b) {
const varA = resolvePath(key, a)
const varB = resolvePath(key, b)
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
if (order === 'desc') {
comparison *= -1
}
return comparison
}
// This function is used to resolve the path of a key in an object
function resolvePath(path, obj) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : null
}, obj || self)
}
console.log(arrayOfObjects.sort(sortByKey([
{ key: 'details.occupation' },
{ key: 'age', order: 'desc' }
])))
CodePudding user response:
You can try :
const arrayOfObjects = [{
id: 3,
name: 'Jane',
age: 25,
city: 'Paris',
details: {
occupation: 'designer',
}
},
{
id: 1,
name: 'John',
age: 30,
city: 'New York',
details: {
occupation: 'developer',
}
},
{
id: 2,
name: 'Bob',
age: 25,
city: 'New York',
details: {
occupation: 'developer',
}
},
];
let sortByKey = function (a, b, keys) {
for (let key of keys) {
if (a[key.key] != b[key.key]) return (a[key.key] > b[key.key] ? 1 : -1) * (key.order == 'desc' ? -1 : 1);
}
return 0;
}
let result = arrayOfObjects.sort((a, b) => sortByKey(a, b, [{ key: 'age' }, { key: 'city', order: 'desc' }]));
console.log(result);