From this original question, how would I apply a sort on multiple fields?
Using this slightly adapted structure, how would I sort city (ascending) & then price (
Here's a generic multidimensional sort, allowing for reversing and/or mapping on each level.
Written in Typescript. For Javascript, check out this JSFiddle
type itemMap = (n: any) => any;
interface SortConfig {
key: keyof T;
reverse?: boolean;
map?: itemMap;
}
export function byObjectValues(keys: ((keyof T) | SortConfig)[]): (a: T, b: T) => 0 | 1 | -1 {
return function(a: T, b: T) {
const firstKey: keyof T | SortConfig = keys[0];
const isSimple = typeof firstKey === 'string';
const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig).key;
const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig).reverse;
const map: itemMap | null = isSimple ? null : (firstKey as SortConfig).map || null;
const valA = map ? map(a[key]) : a[key];
const valB = map ? map(b[key]) : b[key];
if (valA === valB) {
if (keys.length === 1) {
return 0;
}
return byObjectValues(keys.slice(1))(a, b);
}
if (reverse) {
return valA > valB ? -1 : 1;
}
return valA > valB ? 1 : -1;
};
}
Sorting a people array by last name, then first name:
interface Person {
firstName: string;
lastName: string;
}
people.sort(byObjectValues(['lastName','firstName']));
Sort language codes by their name, not their language code (see map
), then by descending version (see reverse
).
interface Language {
code: string;
version: number;
}
// languageCodeToName(code) is defined elsewhere in code
languageCodes.sort(byObjectValues([
{
key: 'code',
map(code:string) => languageCodeToName(code),
},
{
key: 'version',
reverse: true,
}
]));