UNPKG

@stnekroman/tstools

Version:

Set of handy tools for TypeScript development

121 lines (105 loc) 4.41 kB
import { Functions } from './Functions'; import { Objects } from "./Objects"; import { Types } from "./Types"; export type SortableType = string | number | Date | boolean; export class Sorter<T> { public static readonly COMPARATORS = { get number() { return (a: number, b: number) => a - b; }, get string() { return (a: string, b: string, stringCompareOptions ?: Intl.CollatorOptions) => a.localeCompare(b, undefined, stringCompareOptions); }, get boolean() { return (a: boolean, b: boolean) => { if (!a && b) { return 1; } else if (a && !b) { return -1; } else { return 0; } }; }, get Date() { return (a: Date, b: Date) => { return a.getTime() - b.getTime(); }; }, get null() { return (a: SortableType, b: SortableType) => { if (!Objects.isNotNullOrUndefined(a) && Objects.isNotNullOrUndefined(b)) { return 1; } else if (Objects.isNotNullOrUndefined(a) && !Objects.isNotNullOrUndefined) { return -1; } else { return 0; } }; } }; private _nullsLast : boolean = true; private _stringCollatorOptions ?: Intl.CollatorOptions; private _inverse : boolean = false; private readonly extractor : Functions.MapFunction<T, T[keyof T] & SortableType>; public static byField<T>(fieldname : keyof T & Types.KeysWithValsOfType<T, SortableType>) : Sorter<T> { return new Sorter<T>(fieldname); } public static byExtractor<T>(extractor : Functions.MapFunction<T, T[keyof T] & SortableType>) : Sorter<T> { return new Sorter(extractor); } private constructor(extractorOrFieldname : Functions.MapFunction<T, T[keyof T] & SortableType> | (keyof T & Types.KeysWithValsOfType<T, SortableType>)) { if (Objects.isFunction(extractorOrFieldname)) { this.extractor = extractorOrFieldname; } else { this.extractor = Functions.extractor(extractorOrFieldname) as Functions.MapFunction<T, T[keyof T] & SortableType>; } } public nullsLast(value : boolean) : this { this._nullsLast = value; return this; } public inverse() : this { this._inverse = !this._inverse; return this; } public stringCollatorOptions(value : Intl.CollatorOptions) : this { this._stringCollatorOptions = value; return this; } public build(nextSort ?: Functions.Comparator<T>) : Functions.Comparator<T> { return (a: T, b: T) : number => { const avalue : SortableType = this.extractor(a); const bvalue : SortableType = this.extractor(b); const result = this.compareValues(avalue, bvalue); if (result === 0 && Objects.isNotNullOrUndefined(nextSort)) { return nextSort(a, b); } return result; }; } private compareValues(avalue: SortableType, bvalue: SortableType) : number { const nullCheck : number = this._nullsLast ? Sorter.COMPARATORS.null(avalue, bvalue) : -1 * Sorter.COMPARATORS.null(avalue, bvalue); if (nullCheck !== 0) { return nullCheck; } let result : number; if (Objects.isString(avalue) && Objects.isString(bvalue)) { result = Sorter.COMPARATORS.string(avalue, bvalue, this._stringCollatorOptions); } else if (Objects.isNumeric(avalue) && Objects.isNumeric(bvalue)) { result = Sorter.COMPARATORS.number(avalue, bvalue); } else if (Objects.isBoolean(avalue) && Objects.isBoolean(bvalue)) { result = Sorter.COMPARATORS.boolean(avalue, bvalue); } else if (avalue instanceof Date && bvalue instanceof Date) { result = Sorter.COMPARATORS.Date(avalue, bvalue); } else if (!Objects.isNotNullOrUndefined && !Objects.isNotNullOrUndefined(bvalue)) { result = nullCheck; } else { throw new Error("Not supported combination of types"); } if (this._inverse) { result = -1 * result; } return result; } }