sync
This commit is contained in:
245
src/comparer/async.ts
Normal file
245
src/comparer/async.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { MaybeAsyncConverter } from "../types.js";
|
||||
import { Nullable } from "../utils.js";
|
||||
import { AsyncComparer, MaybeAsyncComparisonOrComparer, Comparer, MaybeAsyncComparison, AsyncComparison } from "./types.js";
|
||||
|
||||
export function isAsyncComparer<T>(obj: any): obj is AsyncComparer<T> {
|
||||
return obj instanceof BaseAsyncComparer;
|
||||
}
|
||||
|
||||
export function asAsyncComparer<T>(comparer: MaybeAsyncComparisonOrComparer<T>): AsyncComparer<T> {
|
||||
return typeof comparer === "function" ? createAsyncComparer(comparer) : isAsyncComparer<T>(comparer) ? comparer : new WrappedAsyncComparer(comparer);
|
||||
}
|
||||
|
||||
export function fromSync<T>(comparer: Comparer<T>): AsyncComparer<T> {
|
||||
return new WrappedAsyncComparer(comparer);
|
||||
}
|
||||
|
||||
export function createAsyncComparer<T>(comparison: MaybeAsyncComparison<T>): AsyncComparer<T> {
|
||||
return new SimpleAsyncComparer(comparison);
|
||||
}
|
||||
|
||||
export function createAsyncComparerUsing<T, U>(projection: MaybeAsyncConverter<T, U>, comparison?: MaybeAsyncComparisonOrComparer<U>): AsyncComparer<T> {
|
||||
return new MappedAsyncComparer(projection, comparison);
|
||||
}
|
||||
|
||||
export function combineNullableAsyncComparers<T>(comparers: Nullable<MaybeAsyncComparisonOrComparer<T>>[]): AsyncComparer<T> | undefined {
|
||||
let result = defaultAsyncComparer;
|
||||
|
||||
for (const comparer of comparers) {
|
||||
if (!comparer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = result.then(asAsyncComparer(comparer));
|
||||
}
|
||||
|
||||
return result === defaultAsyncComparer ? undefined : result;
|
||||
}
|
||||
|
||||
export abstract class BaseAsyncComparer<T> implements AsyncComparer<T> {
|
||||
#cachedBoundComparison: AsyncComparison<T> | undefined;
|
||||
|
||||
public abstract compare(a: T, b: T): Promise<number>;
|
||||
|
||||
public comparison(): AsyncComparison<T> {
|
||||
return this.#cachedBoundComparison ??= this.compare.bind(this);
|
||||
}
|
||||
|
||||
public reverse(): AsyncComparer<T> {
|
||||
return new ReversedAsyncComparer(this);
|
||||
}
|
||||
|
||||
public then(comparer: AsyncComparer<T>): AsyncComparer<T> {
|
||||
return new ThenAsyncComparer(this, comparer);
|
||||
}
|
||||
|
||||
public thenCompare(comparison: MaybeAsyncComparison<T>): AsyncComparer<T> {
|
||||
return this.then(createAsyncComparer(comparison));
|
||||
}
|
||||
|
||||
public thenCompareUsing<U>(projection: MaybeAsyncConverter<T, U>, comparer?: MaybeAsyncComparisonOrComparer<U>): AsyncComparer<T> {
|
||||
return this.then(createAsyncComparerUsing(projection, comparer));
|
||||
}
|
||||
|
||||
public nullAwareComparer(): AsyncComparer<Nullable<T>> {
|
||||
return new NullAwareAsyncComparer(this);
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedAsyncComparer<T> extends BaseAsyncComparer<T> {
|
||||
readonly #base: Comparer<T>;
|
||||
|
||||
constructor(base: Comparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = base;
|
||||
}
|
||||
|
||||
public override async compare(a: T, b: T) {
|
||||
return this.#base.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleAsyncComparer<T> extends BaseAsyncComparer<T> {
|
||||
readonly #comparison: MaybeAsyncComparison<T>;
|
||||
|
||||
public constructor(comparison: MaybeAsyncComparison<T>) {
|
||||
super();
|
||||
|
||||
this.#comparison = comparison;
|
||||
}
|
||||
|
||||
public async compare(a: T, b: T) {
|
||||
return await this.#comparison(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
class MappedAsyncComparer<T, U> extends BaseAsyncComparer<T> {
|
||||
readonly #projection: MaybeAsyncConverter<T, U>;
|
||||
readonly #comparer: AsyncComparer<U>;
|
||||
|
||||
public constructor(projection: MaybeAsyncConverter<T, U>, comparer?: MaybeAsyncComparisonOrComparer<U>) {
|
||||
super();
|
||||
|
||||
this.#projection = projection;
|
||||
this.#comparer = comparer ? asAsyncComparer(comparer) : defaultAsyncComparer;
|
||||
}
|
||||
|
||||
public async compare(a: T, b: T) {
|
||||
return await this.#comparer.compare(await this.#projection(a), await this.#projection(b));
|
||||
}
|
||||
}
|
||||
|
||||
class ReversedAsyncComparer<T> extends BaseAsyncComparer<T> {
|
||||
readonly #base: AsyncComparer<T>;
|
||||
|
||||
public constructor(base: AsyncComparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = base;
|
||||
}
|
||||
|
||||
public async compare(a: T, b: T) {
|
||||
return await this.#base.compare(b, a);
|
||||
}
|
||||
|
||||
public override reverse() {
|
||||
return this.#base;
|
||||
}
|
||||
}
|
||||
|
||||
class ThenAsyncComparer<T> extends BaseAsyncComparer<T> {
|
||||
readonly #base: AsyncComparer<T>;
|
||||
readonly #comparer: AsyncComparer<T>;
|
||||
|
||||
public constructor(base: AsyncComparer<T>, comparer: AsyncComparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = base;
|
||||
this.#comparer = comparer;
|
||||
}
|
||||
|
||||
public async compare(a: T, b: T) {
|
||||
return await this.#base.compare(a, b) || await this.#comparer.compare(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultAsyncComparer: AsyncComparer<any> = new class DefaultAsyncComparer extends BaseAsyncComparer<any> {
|
||||
public override async compare(a: any, b: any): Promise<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 getDefaultAsyncComparer<T>(): AsyncComparer<T> {
|
||||
return defaultAsyncComparer;
|
||||
}
|
||||
|
||||
class NullAwareAsyncComparer<T> extends BaseAsyncComparer<Nullable<T>> {
|
||||
readonly #base: AsyncComparer<T>;
|
||||
|
||||
constructor(baseComparer: AsyncComparer<T>) {
|
||||
super();
|
||||
|
||||
this.#base = baseComparer;
|
||||
}
|
||||
|
||||
public override async compare(a: Nullable<T>, b: Nullable<T>): Promise<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 await this.#base.compare(a, b);
|
||||
}
|
||||
|
||||
public override nullAwareComparer(): AsyncComparer<Nullable<T>> {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function nullAwareComparer<T>(baseComparer: AsyncComparer<T>): AsyncComparer<Nullable<T>> {
|
||||
return new NullAwareAsyncComparer(baseComparer);
|
||||
}
|
||||
|
||||
export const stringAsyncComparer: AsyncComparer<string> = new class StringAsyncComparer extends BaseAsyncComparer<string> {
|
||||
public override async compare(a: string, b: string): Promise<number> {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
};
|
||||
|
||||
export const numberAsyncComparer: AsyncComparer<number> = new class NumberAsyncComparer extends BaseAsyncComparer<number> {
|
||||
public override async compare(a: number, b: number): Promise<number> {
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
export const bigintAsyncComparer: AsyncComparer<bigint> = new class BigIntAsyncComparer extends BaseAsyncComparer<bigint> {
|
||||
public override async compare(a: bigint, b: bigint): Promise<number> {
|
||||
return Number(a - b);
|
||||
}
|
||||
};
|
||||
|
||||
export const booleanAsyncComparer: AsyncComparer<boolean> = new class BooleanAsyncComparer extends BaseAsyncComparer<boolean> {
|
||||
public override async compare(a: boolean, b: boolean): Promise<number> {
|
||||
return Number(a) - Number(b);
|
||||
}
|
||||
};
|
||||
|
||||
export const dateAsyncComparer: AsyncComparer<Date> = new class DateAsyncComparer extends BaseAsyncComparer<Date> {
|
||||
public override async compare(a: Date, b: Date): Promise<number> {
|
||||
return a.getTime() - b.getTime();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user