sync
This commit is contained in:
244
src/comparer/sync.ts
Normal file
244
src/comparer/sync.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { Converter } from "../types.js";
|
||||
import { Nullable } from "../utils.js";
|
||||
import { fromSync } from "./async.js";
|
||||
import { Comparer, ComparisonOrComparer, Comparison, AsyncComparer } from "./types.js";
|
||||
|
||||
export function isComparer<T>(obj: any): obj is Comparer<T> {
|
||||
return obj instanceof BaseComparer;
|
||||
}
|
||||
|
||||
export function asComparer<T>(comparer: ComparisonOrComparer<T>) {
|
||||
return typeof comparer === "function" ? createComparer(comparer) : comparer;
|
||||
}
|
||||
|
||||
export function asComparison<T>(comparer: ComparisonOrComparer<T>) {
|
||||
return typeof comparer === "function" ? comparer : comparer.comparison();
|
||||
}
|
||||
|
||||
export function createComparer<T>(comparison: Comparison<T>): Comparer<T> {
|
||||
return new SimpleComparer(comparison);
|
||||
}
|
||||
|
||||
export function createComparerUsing<T, U>(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>): Comparer<T> {
|
||||
return new MappedComparer(projection, comparison);
|
||||
}
|
||||
|
||||
export function reverseComparison<T>(comparison: Comparison<T>): Comparison<T> {
|
||||
return (a, b) => comparison(b, a);
|
||||
}
|
||||
|
||||
export function combineNullableComparers<T>(comparers: Nullable<ComparisonOrComparer<T>>[]) {
|
||||
let result = defaultComparer;
|
||||
|
||||
for (const comparer of comparers) {
|
||||
if (!comparer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = result.then(asComparer(comparer));
|
||||
}
|
||||
|
||||
return result === defaultComparer ? undefined : result;
|
||||
}
|
||||
|
||||
export abstract class BaseComparer<T> implements Comparer<T> {
|
||||
#cachedBoundComparison: Comparison<T> | undefined;
|
||||
|
||||
public abstract compare(a: T, b: T): number;
|
||||
|
||||
public comparison(): Comparison<T> {
|
||||
return this.#cachedBoundComparison ??= this.compare.bind(this);
|
||||
}
|
||||
|
||||
public reverse(): Comparer<T> {
|
||||
return new ReversedComparer(this);
|
||||
}
|
||||
|
||||
public then(comparer: Comparer<T>): Comparer<T> {
|
||||
return new ThenComparer(this, comparer);
|
||||
}
|
||||
|
||||
public thenCompare(comparison: Comparison<T>): Comparer<T> {
|
||||
return this.then(createComparer(comparison));
|
||||
}
|
||||
|
||||
public thenCompareUsing<U>(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>): Comparer<T> {
|
||||
return this.then(createComparerUsing(projection, comparison));
|
||||
}
|
||||
|
||||
public toAsync(): AsyncComparer<T> {
|
||||
return fromSync<T>(this);
|
||||
}
|
||||
|
||||
public nullAwareComparer(): Comparer<Nullable<T>> {
|
||||
return new NullAwareComparer(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleComparer<T> extends BaseComparer<T> {
|
||||
readonly #comparison: Comparison<T>;
|
||||
|
||||
public constructor(comparison: Comparison<T>) {
|
||||
super();
|
||||
|
||||
this.#comparison = comparison;
|
||||
}
|
||||
|
||||
public override compare(a: T, b: T) {
|
||||
return this.#comparison(a, b);
|
||||
}
|
||||
|
||||
public override comparison() {
|
||||
return this.#comparison;
|
||||
}
|
||||
}
|
||||
|
||||
class MappedComparer<T, U> extends BaseComparer<T> {
|
||||
readonly #projection: Converter<T, U>;
|
||||
readonly #comparison: Comparer<U>;
|
||||
|
||||
public constructor(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>) {
|
||||
super();
|
||||
|
||||
this.#projection = projection;
|
||||
this.#comparison = comparison ? asComparer(comparison) : defaultComparer;
|
||||
}
|
||||
|
||||
public override compare(a: T, b: T) {
|
||||
return this.#comparison.compare(this.#projection(a), this.#projection(b));
|
||||
}
|
||||
}
|
||||
|
||||
class ReversedComparer<T> extends BaseComparer<T> {
|
||||
readonly #base: Comparer<T>;
|
||||
|
||||
public constructor(base: Comparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = base;
|
||||
}
|
||||
|
||||
public override compare(a: T, b: T): number {
|
||||
return this.#base.compare(b, a);
|
||||
}
|
||||
|
||||
public override reverse(): Comparer<T> {
|
||||
return this.#base;
|
||||
}
|
||||
}
|
||||
|
||||
class ThenComparer<T> extends BaseComparer<T> {
|
||||
readonly #base: Comparer<T>;
|
||||
readonly #comparer: Comparer<T>;
|
||||
|
||||
public constructor(base: Comparer<T>, comparer: Comparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = base;
|
||||
this.#comparer = comparer;
|
||||
}
|
||||
|
||||
public override compare(a: T, b: T) {
|
||||
return this.#base.compare(a, b) || this.#comparer.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultComparer: Comparer<any> = new class DefaultComparer extends BaseComparer<any> {
|
||||
public override compare(a: any, b: any): number {
|
||||
if (a === undefined) {
|
||||
if (b === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (b === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const aStr = `${a}`, bStr = `${b}`;
|
||||
|
||||
return aStr > bStr ? 1 : aStr < bStr ? -1 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
export function getDefaultComparer<T>(): Comparer<T> {
|
||||
return defaultComparer;
|
||||
}
|
||||
|
||||
class NullAwareComparer<T> extends BaseComparer<Nullable<T>> {
|
||||
readonly #base: Comparer<T>;
|
||||
|
||||
constructor(baseComparer: Comparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = baseComparer;
|
||||
}
|
||||
|
||||
public override compare(a: Nullable<T>, b: Nullable<T>): number {
|
||||
if (a === undefined) {
|
||||
if (b === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (b === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a === null) {
|
||||
if (b === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (b === null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.#base.compare(a, b);
|
||||
}
|
||||
|
||||
public override nullAwareComparer(): Comparer<Nullable<T>> {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function nullAwareComparer<T>(baseComparer: Comparer<T>): Comparer<Nullable<T>> {
|
||||
return new NullAwareComparer(baseComparer);
|
||||
}
|
||||
|
||||
export const stringComparer: Comparer<string> = new class StringComparer extends BaseComparer<string> {
|
||||
public override compare(a: string, b: string): number {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
};
|
||||
|
||||
export const numberComparer: Comparer<number> = new class NumberComparer extends BaseComparer<number> {
|
||||
public override compare(a: number, b: number): number {
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
export const bigintComparer: Comparer<bigint> = new class BigIntComparer extends BaseComparer<bigint> {
|
||||
public override compare(a: bigint, b: bigint): number {
|
||||
return Number(a - b);
|
||||
}
|
||||
};
|
||||
|
||||
export const booleanComparer: Comparer<boolean> = new class BooleanComparer extends BaseComparer<boolean> {
|
||||
public override compare(a: boolean, b: boolean): number {
|
||||
return Number(a) - Number(b);
|
||||
}
|
||||
};
|
||||
|
||||
export const dateComparer: Comparer<Date> = new class DateComparer extends BaseComparer<Date> {
|
||||
public override compare(a: Date, b: Date): number {
|
||||
return a.getTime() - b.getTime();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user