Typescript / javascript OrderBy

Date: 2021-09-21
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]);
53250cookie-checkTypescript / javascript OrderBy