From 8a2c3f8e98d66e73c86dc7c18026c3bf15e573dc Mon Sep 17 00:00:00 2001 From: Herve BECHER Date: Tue, 28 Oct 2025 16:12:34 +0100 Subject: [PATCH] properly implement comparer and equality comparer combination --- src/async/impl.ts | 6 ++-- src/comparer/async.ts | 24 ++++++++++++++-- src/comparer/sync.ts | 24 ++++++++++++++-- src/equality-comparer/async.ts | 50 ++++++++++++++++++++++++++++++++-- src/equality-comparer/sync.ts | 50 ++++++++++++++++++++++++++++++++-- src/sync/impl.ts | 6 ++-- 6 files changed, 142 insertions(+), 18 deletions(-) diff --git a/src/async/impl.ts b/src/async/impl.ts index 965d078..7a53739 100644 --- a/src/async/impl.ts +++ b/src/async/impl.ts @@ -1,5 +1,5 @@ import { Collector } from "../collector/types.js"; -import { asAsyncComparer, combineNullableAsyncComparers, createAsyncComparerUsing, defaultAsyncComparer } from "../comparer/async.js"; +import { asAsyncComparer, combineAsyncComparers, createAsyncComparerUsing, defaultAsyncComparer } from "../comparer/async.js"; import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "../comparer/types.js"; import { asAsyncEqualityComparer, defaultAsyncEqualityComparer } from "../equality-comparer/async.js"; import { AsyncEqualityComparer, MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js"; @@ -1713,13 +1713,13 @@ class OrderByAsyncSequence extends BaseOrderedAsyncSequence { class ThenOrderAsyncSequence extends BaseOrderedAsyncSequence { constructor(sequence: OrderedAsyncSequence, descending: boolean, sorter?: MaybeAsyncComparisonOrComparer) { - super(sequence, combineNullableAsyncComparers([sequence.comparer, sorter]), descending); + super(sequence, combineAsyncComparers([sequence.comparer, sorter]), descending); } } class ThenOrderByAsyncSequence extends BaseOrderedAsyncSequence { constructor(sequence: OrderedAsyncSequence, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparisonOrComparer) { - super(sequence, combineNullableAsyncComparers([sequence.comparer, createAsyncComparerUsing(selector, sorter)]), descending); + super(sequence, combineAsyncComparers([sequence.comparer, createAsyncComparerUsing(selector, sorter)]), descending); } } diff --git a/src/comparer/async.ts b/src/comparer/async.ts index 23c5408..4fab780 100644 --- a/src/comparer/async.ts +++ b/src/comparer/async.ts @@ -22,8 +22,8 @@ export function createAsyncComparerUsing(projection: MaybeAsyncConverter(comparers: Nullable>[]): AsyncComparer | undefined { - let result = defaultAsyncComparer; +export function combineAsyncComparers(comparers: Iterable>>): AsyncComparer | undefined { + let result: AsyncComparer = dummyAsyncComparer; for (const comparer of comparers) { if (!comparer) { @@ -33,7 +33,7 @@ export function combineNullableAsyncComparers(comparers: Nullable implements AsyncComparer { @@ -144,6 +144,24 @@ class ThenAsyncComparer extends BaseAsyncComparer { } } +const dummyAsyncComparer = new class DummyAsyncComparer extends BaseAsyncComparer { + public override async compare(_a: any, _b: any): Promise { + return 0; + } + + public override then(comparer: AsyncComparer): AsyncComparer { + return comparer; + } + + public override thenCompare(comparison: MaybeAsyncComparison): AsyncComparer { + return createAsyncComparer(comparison); + } + + public override thenCompareUsing(projection: MaybeAsyncConverter, comparison?: MaybeAsyncComparisonOrComparer | undefined): AsyncComparer { + return createAsyncComparerUsing(projection, comparison); + } +} + export const defaultAsyncComparer: AsyncComparer = new class DefaultAsyncComparer extends BaseAsyncComparer { public override async compare(a: any, b: any): Promise { if (a === undefined) { diff --git a/src/comparer/sync.ts b/src/comparer/sync.ts index f5cb18e..7c1ccac 100644 --- a/src/comparer/sync.ts +++ b/src/comparer/sync.ts @@ -27,8 +27,8 @@ export function reverseComparison(comparison: Comparison): Comparison { return (a, b) => comparison(b, a); } -export function combineNullableComparers(comparers: Nullable>[]) { - let result = defaultComparer; +export function combineComparers(comparers: Iterable>>) { + let result: Comparer = dummyComparer; for (const comparer of comparers) { if (!comparer) { @@ -38,7 +38,7 @@ export function combineNullableComparers(comparers: Nullable implements Comparer { @@ -75,6 +75,24 @@ export abstract class BaseComparer implements Comparer { } } +const dummyComparer = new class DummyComparer extends BaseComparer { + public override compare(_a: any, _b: any): number { + return 0; + } + + public override then(comparer: Comparer): Comparer { + return comparer; + } + + public override thenCompare(comparison: Comparison): Comparer { + return createComparer(comparison); + } + + public override thenCompareUsing(projection: Converter, comparison?: ComparisonOrComparer | undefined): Comparer { + return createComparerUsing(projection, comparison); + } +}; + class SimpleComparer extends BaseComparer { readonly #comparison: Comparison; diff --git a/src/equality-comparer/async.ts b/src/equality-comparer/async.ts index 02fc651..5e99ea8 100644 --- a/src/equality-comparer/async.ts +++ b/src/equality-comparer/async.ts @@ -22,8 +22,8 @@ export function createAsyncEqualityComparerUsing(projection: M return new MappedAsyncEqualityComparer(projection, equalityComparison); } -export function combineNullableAsyncEqualityComparers(equalityComparers: Nullable>[]) { - let result = defaultAsyncEqualityComparer; +export function combineAsyncEqualityComparers(equalityComparers: Iterable>>) { + let result: AsyncEqualityComparer = alwaysTrueAsyncEqualityComparer; for (const equalityComparer of equalityComparers) { if (!equalityComparer) { @@ -33,7 +33,7 @@ export function combineNullableAsyncEqualityComparers(equalityComparers: Null result = result.then(asAsyncEqualityComparer(equalityComparer)); } - return result === defaultAsyncEqualityComparer ? undefined : result; + return result === alwaysTrueAsyncEqualityComparer ? undefined : result; } export abstract class BaseAsyncEqualityComparer implements AsyncEqualityComparer { @@ -140,6 +140,50 @@ class ThenAsyncEqualityComparer extends BaseAsyncEqualityComparer { } } +export const alwaysAsyncFalseEqualityComparer = new class AlwaysFalseAsyncEqualityComparer extends BaseAsyncEqualityComparer { + public override async equals(_a: any, _b: any): Promise { + return false; + } + + public override opposite(): AsyncEqualityComparer { + return alwaysTrueAsyncEqualityComparer; + } + + public override then(_equalityComparer: AsyncEqualityComparer): AsyncEqualityComparer { + return this; + } + + public override thenEquals(_equalityComparison: MaybeAsyncEqualityComparison): AsyncEqualityComparer { + return this; + } + + public override thenEqualsUsing(_projection: MaybeAsyncConverter, _equalityComparison?: MaybeAsyncEqualityComparisonOrComparer | undefined): AsyncEqualityComparer { + return this; + } +}; + +export const alwaysTrueAsyncEqualityComparer = new class AlwaysTrueAsyncEqualityComparer extends BaseAsyncEqualityComparer { + public override async equals(_a: any, _b: any): Promise { + return true; + } + + public override opposite(): AsyncEqualityComparer { + return alwaysAsyncFalseEqualityComparer; + } + + public override then(equalityComparer: AsyncEqualityComparer): AsyncEqualityComparer { + return equalityComparer; + } + + public override thenEquals(equalityComparison: MaybeAsyncEqualityComparison): AsyncEqualityComparer { + return createAsyncEqualityComparer(equalityComparison); + } + + public override thenEqualsUsing(projection: MaybeAsyncConverter, equalityComparison?: MaybeAsyncEqualityComparisonOrComparer | undefined): AsyncEqualityComparer { + return createAsyncEqualityComparerUsing(projection, equalityComparison); + } +}; + export const looseAsyncEqualityComparer: AsyncEqualityComparer = new class LooseAsyncEqualityComparer extends BaseAsyncEqualityComparer { public override async equals(a: any, b: any): Promise { return a == b; diff --git a/src/equality-comparer/sync.ts b/src/equality-comparer/sync.ts index d1cf0f1..cdca6c0 100644 --- a/src/equality-comparer/sync.ts +++ b/src/equality-comparer/sync.ts @@ -18,8 +18,8 @@ export function createEqualityComparerUsing(projection: Conver return new MappedEqualityComparer(projection, equalityComparison); } -export function combineNullableEqualityComparers(equalityComparers: Nullable>[]) { - let result = defaultEqualityComparer; +export function combineEqualityComparers(equalityComparers: Iterable>>) { + let result: EqualityComparer = alwaysTrueEqualityComparer; for (const equalityComparer of equalityComparers) { if (!equalityComparer) { @@ -29,7 +29,7 @@ export function combineNullableEqualityComparers(equalityComparers: Nullable< result = result.then(asEqualityComparer(equalityComparer)); } - return result === defaultEqualityComparer ? undefined : result; + return result === alwaysTrueEqualityComparer ? undefined : result; } export abstract class BaseEqualityComparer implements EqualityComparer { @@ -126,6 +126,50 @@ class ThenEqualityComparer extends BaseEqualityComparer { } } +export const alwaysFalseEqualityComparer = new class AlwaysFalseEqualityComparer extends BaseEqualityComparer { + public override equals(_a: any, _b: any): boolean { + return false; + } + + public override opposite(): EqualityComparer { + return alwaysTrueEqualityComparer; + } + + public override then(_equalityComparer: EqualityComparer): EqualityComparer { + return this; + } + + public override thenEquals(_equalityComparison: EqualityComparison): EqualityComparer { + return this; + } + + public override thenEqualsUsing(_projection: Converter, _equalityComparison?: EqualityComparisonOrComparer | undefined): EqualityComparer { + return this; + } +}; + +export const alwaysTrueEqualityComparer = new class AlwaysTrueEqualityComparer extends BaseEqualityComparer { + public override equals(_a: any, _b: any): boolean { + return true; + } + + public override opposite(): EqualityComparer { + return alwaysFalseEqualityComparer; + } + + public override then(equalityComparer: EqualityComparer): EqualityComparer { + return equalityComparer; + } + + public override thenEquals(equalityComparison: EqualityComparison): EqualityComparer { + return createEqualityComparer(equalityComparison); + } + + public override thenEqualsUsing(projection: Converter, equalityComparison?: EqualityComparisonOrComparer | undefined): EqualityComparer { + return createEqualityComparerUsing(projection, equalityComparison); + } +}; + export const looseEqualityComparer: EqualityComparer = new class LooseEqualityComparer extends BaseEqualityComparer { public override equals(a: any, b: any): boolean { return a == b; diff --git a/src/sync/impl.ts b/src/sync/impl.ts index 2b1e3b0..e000676 100644 --- a/src/sync/impl.ts +++ b/src/sync/impl.ts @@ -1,7 +1,7 @@ import { BaseAsyncSequence } from "../async/impl.js"; import { AsyncSequence } from "../async/types.js"; import { Collector } from "../collector/types.js"; -import { asComparer, combineNullableComparers, createComparerUsing, defaultComparer } from "../comparer/sync.js"; +import { asComparer, combineComparers, createComparerUsing, defaultComparer } from "../comparer/sync.js"; import { ComparisonOrComparer, Comparer } from "../comparer/types.js"; import { asEqualityComparer, defaultEqualityComparer } from "../equality-comparer/sync.js"; import { EqualityComparer, EqualityComparisonOrComparer } from "../equality-comparer/types.js"; @@ -1974,13 +1974,13 @@ class OrderBySequence extends BaseOrderedSequence { class ThenOrderSequence extends BaseOrderedSequence { constructor(sequence: OrderedSequence, descending: boolean, sorter?: ComparisonOrComparer) { - super(sequence, combineNullableComparers([sequence.comparer, sorter]), descending); + super(sequence, combineComparers([sequence.comparer, sorter]), descending); } } class ThenOrderBySequence extends BaseOrderedSequence { constructor(sequence: OrderedSequence, descending: boolean, selector: Converter, sorter?: ComparisonOrComparer) { - super(sequence, combineNullableComparers([sequence.comparer, createComparerUsing(selector, sorter)]), descending); + super(sequence, combineComparers([sequence.comparer, createComparerUsing(selector, sorter)]), descending); } }