I have an array of objects of the following form:
arr[0] = { \'item1\' : 1234, \'item2\' : \'a string\' };
I sort it first by \'item1\'>
I hit the same question lately. Came with a similar solution than langpavel, but I prefer to split the thing in two. First a chained comparator helper that will allows multiple sort rule, each applied in order as a tie-breaker in case of equality:
type Comparator = (a: T, b: T) => number; // -1 | 0 | 1
/**
* Allow to chain multiple comparators, each one called to break equality from the previous one.
*/
function chainedComparator(...comparators: Comparator[]): Comparator {
return (a: T, b: T) => {
let order = 0;
let i = 0;
while (!order && comparators[i]) {
order = comparators[i++](a, b);
}
return order;
};
}
I like it, because it takes and return sort comparator. So if you have a collection of other comparators, they are easy to use.
Then you can simplify a bit your life with an additional helper. This one return a sort comparator based on the result of the passed lambda over each items.
type Comparable = string | number;
/**
* Returns a comparator which use an evaluationFunc on each item for comparison
*/
function lambdaComparator(evaluationFunc: ((item: T) => Comparable), reversed = false): Comparator {
return (a: T, b: T) => {
const valA = evaluationFunc(a);
const valB = evaluationFunc(b);
let order = 0;
if (valA < valB) {
order = -1;
} else if (valA > valB) {
order = 1;
}
return reversed ? -order : order;
};
}
reversed
here is not required to answer the question, but will allow to reverse the order easily.
To answer the question specifically, using our two comparators:
arr.sort(chainedComparator(
lambdaComparator(a => a.item1),
lambdaComparator(a => a.item2.toLowerCase()) // "banana" before "Melon"
));
Because the original question was in pure JavaScript, precision: If you're not accustomed to TypeScript, you can get normal JavaScript just by removing the typing
, : T
, : ((item: T) => Comparable)
everywhere and the two type
lines out.