const isSet = (x: any) => x != null; // works for [null, undefined], [ false, 0, Nan ] are true const isNumber = (x: any) => typeof x === "number"; const isString = (x: any) => typeof x === "string"; const isIterable = (value: any) => Symbol.iterator in Object(value); const stringComparer = (locales?: any, options?: any) => { if (!locales) locales = "en"; if (!options) options = { numeric: true, sensitivity: "base" }; const collator = new Intl.Collator(locales, options); return (a: string, b: string) => collator.compare(a, b); }; const comparer = stringComparer(); function compare(a: any, b: any, reverse: boolean): number { const [x, y] = reverse ? [b, a] : [a, b]; if (!isSet(x) && !isSet(y)) return 0; if (!isSet(x) && isSet(y)) return -1; if (!isSet(y) && isSet(x)) return 1; if (isNumber(x) && isNumber(y)) return x - y; if (isString(x) && isString(y)) return comparer(x, y); if (isIterable(x) && isIterable(y)) { for (const d of Array.from(zipIterables(x, y))) { const c = compare(d[0], d[1], reverse); if (c !== 0) return c; } return 0; } return comparer(String(x), String(y)); } export function orderBy<T>(array: T[], fn: (x: T) => any, reverse?: boolean) { const result = Array.from(array); result.sort((a, b) => compare(fn(a), fn(b), reverse || false)); return result; }; // Example use: // Sort events first on 'start' date property, when start equals compare the 'end' property orderBy(events, (e) => [e.start, e.end]);
532520cookie-checkTypescript / javascript OrderBy